roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {  
69                 document.createEvent("TouchEvent");  
70                 return true;  
71             } catch (e) {  
72                 return false;  
73             } 
74             
75         })();
76     // remove css image flicker
77         if(isIE && !isIE7){
78         try{
79             document.execCommand("BackgroundImageCache", false, true);
80         }catch(e){}
81     }
82     
83     Roo.apply(Roo, {
84         /**
85          * True if the browser is in strict mode
86          * @type Boolean
87          */
88         isStrict : isStrict,
89         /**
90          * True if the page is running over SSL
91          * @type Boolean
92          */
93         isSecure : isSecure,
94         /**
95          * True when the document is fully initialized and ready for action
96          * @type Boolean
97          */
98         isReady : false,
99         /**
100          * Turn on debugging output (currently only the factory uses this)
101          * @type Boolean
102          */
103         
104         debug: false,
105
106         /**
107          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
108          * @type Boolean
109          */
110         enableGarbageCollector : true,
111
112         /**
113          * True to automatically purge event listeners after uncaching an element (defaults to false).
114          * Note: this only happens if enableGarbageCollector is true.
115          * @type Boolean
116          */
117         enableListenerCollection:false,
118
119         /**
120          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
121          * the IE insecure content warning (defaults to javascript:false).
122          * @type String
123          */
124         SSL_SECURE_URL : "javascript:false",
125
126         /**
127          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
128          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
129          * @type String
130          */
131         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
132
133         emptyFn : function(){},
134         
135         /**
136          * Copies all the properties of config to obj if they don't already exist.
137          * @param {Object} obj The receiver of the properties
138          * @param {Object} config The source of the properties
139          * @return {Object} returns obj
140          */
141         applyIf : function(o, c){
142             if(o && c){
143                 for(var p in c){
144                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
145                 }
146             }
147             return o;
148         },
149
150         /**
151          * Applies event listeners to elements by selectors when the document is ready.
152          * The event name is specified with an @ suffix.
153 <pre><code>
154 Roo.addBehaviors({
155    // add a listener for click on all anchors in element with id foo
156    '#foo a@click' : function(e, t){
157        // do something
158    },
159
160    // add the same listener to multiple selectors (separated by comma BEFORE the @)
161    '#foo a, #bar span.some-class@mouseover' : function(){
162        // do something
163    }
164 });
165 </code></pre>
166          * @param {Object} obj The list of behaviors to apply
167          */
168         addBehaviors : function(o){
169             if(!Roo.isReady){
170                 Roo.onReady(function(){
171                     Roo.addBehaviors(o);
172                 });
173                 return;
174             }
175             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
176             for(var b in o){
177                 var parts = b.split('@');
178                 if(parts[1]){ // for Object prototype breakers
179                     var s = parts[0];
180                     if(!cache[s]){
181                         cache[s] = Roo.select(s);
182                     }
183                     cache[s].on(parts[1], o[b]);
184                 }
185             }
186             cache = null;
187         },
188
189         /**
190          * Generates unique ids. If the element already has an id, it is unchanged
191          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
192          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
193          * @return {String} The generated Id.
194          */
195         id : function(el, prefix){
196             prefix = prefix || "roo-gen";
197             el = Roo.getDom(el);
198             var id = prefix + (++idSeed);
199             return el ? (el.id ? el.id : (el.id = id)) : id;
200         },
201          
202        
203         /**
204          * Extends one class with another class and optionally overrides members with the passed literal. This class
205          * also adds the function "override()" to the class that can be used to override
206          * members on an instance.
207          * @param {Object} subclass The class inheriting the functionality
208          * @param {Object} superclass The class being extended
209          * @param {Object} overrides (optional) A literal with members
210          * @method extend
211          */
212         extend : function(){
213             // inline overrides
214             var io = function(o){
215                 for(var m in o){
216                     this[m] = o[m];
217                 }
218             };
219             return function(sb, sp, overrides){
220                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
221                     overrides = sp;
222                     sp = sb;
223                     sb = function(){sp.apply(this, arguments);};
224                 }
225                 var F = function(){}, sbp, spp = sp.prototype;
226                 F.prototype = spp;
227                 sbp = sb.prototype = new F();
228                 sbp.constructor=sb;
229                 sb.superclass=spp;
230                 
231                 if(spp.constructor == Object.prototype.constructor){
232                     spp.constructor=sp;
233                    
234                 }
235                 
236                 sb.override = function(o){
237                     Roo.override(sb, o);
238                 };
239                 sbp.override = io;
240                 Roo.override(sb, overrides);
241                 return sb;
242             };
243         }(),
244
245         /**
246          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
247          * Usage:<pre><code>
248 Roo.override(MyClass, {
249     newMethod1: function(){
250         // etc.
251     },
252     newMethod2: function(foo){
253         // etc.
254     }
255 });
256  </code></pre>
257          * @param {Object} origclass The class to override
258          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
259          * containing one or more methods.
260          * @method override
261          */
262         override : function(origclass, overrides){
263             if(overrides){
264                 var p = origclass.prototype;
265                 for(var method in overrides){
266                     p[method] = overrides[method];
267                 }
268             }
269         },
270         /**
271          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
272          * <pre><code>
273 Roo.namespace('Company', 'Company.data');
274 Company.Widget = function() { ... }
275 Company.data.CustomStore = function(config) { ... }
276 </code></pre>
277          * @param {String} namespace1
278          * @param {String} namespace2
279          * @param {String} etc
280          * @method namespace
281          */
282         namespace : function(){
283             var a=arguments, o=null, i, j, d, rt;
284             for (i=0; i<a.length; ++i) {
285                 d=a[i].split(".");
286                 rt = d[0];
287                 /** eval:var:o */
288                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
289                 for (j=1; j<d.length; ++j) {
290                     o[d[j]]=o[d[j]] || {};
291                     o=o[d[j]];
292                 }
293             }
294         },
295         /**
296          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
297          * <pre><code>
298 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
299 Roo.factory(conf, Roo.data);
300 </code></pre>
301          * @param {String} classname
302          * @param {String} namespace (optional)
303          * @method factory
304          */
305          
306         factory : function(c, ns)
307         {
308             // no xtype, no ns or c.xns - or forced off by c.xns
309             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
310                 return c;
311             }
312             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
313             if (c.constructor == ns[c.xtype]) {// already created...
314                 return c;
315             }
316             if (ns[c.xtype]) {
317                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
318                 var ret = new ns[c.xtype](c);
319                 ret.xns = false;
320                 return ret;
321             }
322             c.xns = false; // prevent recursion..
323             return c;
324         },
325          /**
326          * Logs to console if it can.
327          *
328          * @param {String|Object} string
329          * @method log
330          */
331         log : function(s)
332         {
333             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
334                 return; // alerT?
335             }
336             console.log(s);
337             
338         },
339         /**
340          * 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.
341          * @param {Object} o
342          * @return {String}
343          */
344         urlEncode : function(o){
345             if(!o){
346                 return "";
347             }
348             var buf = [];
349             for(var key in o){
350                 var ov = o[key], k = Roo.encodeURIComponent(key);
351                 var type = typeof ov;
352                 if(type == 'undefined'){
353                     buf.push(k, "=&");
354                 }else if(type != "function" && type != "object"){
355                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
356                 }else if(ov instanceof Array){
357                     if (ov.length) {
358                             for(var i = 0, len = ov.length; i < len; i++) {
359                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
360                             }
361                         } else {
362                             buf.push(k, "=&");
363                         }
364                 }
365             }
366             buf.pop();
367             return buf.join("");
368         },
369          /**
370          * Safe version of encodeURIComponent
371          * @param {String} data 
372          * @return {String} 
373          */
374         
375         encodeURIComponent : function (data)
376         {
377             try {
378                 return encodeURIComponent(data);
379             } catch(e) {} // should be an uri encode error.
380             
381             if (data == '' || data == null){
382                return '';
383             }
384             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
385             function nibble_to_hex(nibble){
386                 var chars = '0123456789ABCDEF';
387                 return chars.charAt(nibble);
388             }
389             data = data.toString();
390             var buffer = '';
391             for(var i=0; i<data.length; i++){
392                 var c = data.charCodeAt(i);
393                 var bs = new Array();
394                 if (c > 0x10000){
395                         // 4 bytes
396                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
397                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
398                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
399                     bs[3] = 0x80 | (c & 0x3F);
400                 }else if (c > 0x800){
401                          // 3 bytes
402                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
403                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
404                     bs[2] = 0x80 | (c & 0x3F);
405                 }else if (c > 0x80){
406                        // 2 bytes
407                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
408                     bs[1] = 0x80 | (c & 0x3F);
409                 }else{
410                         // 1 byte
411                     bs[0] = c;
412                 }
413                 for(var j=0; j<bs.length; j++){
414                     var b = bs[j];
415                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
416                             + nibble_to_hex(b &0x0F);
417                     buffer += '%'+hex;
418                }
419             }
420             return buffer;    
421              
422         },
423
424         /**
425          * 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]}.
426          * @param {String} string
427          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
428          * @return {Object} A literal with members
429          */
430         urlDecode : function(string, overwrite){
431             if(!string || !string.length){
432                 return {};
433             }
434             var obj = {};
435             var pairs = string.split('&');
436             var pair, name, value;
437             for(var i = 0, len = pairs.length; i < len; i++){
438                 pair = pairs[i].split('=');
439                 name = decodeURIComponent(pair[0]);
440                 value = decodeURIComponent(pair[1]);
441                 if(overwrite !== true){
442                     if(typeof obj[name] == "undefined"){
443                         obj[name] = value;
444                     }else if(typeof obj[name] == "string"){
445                         obj[name] = [obj[name]];
446                         obj[name].push(value);
447                     }else{
448                         obj[name].push(value);
449                     }
450                 }else{
451                     obj[name] = value;
452                 }
453             }
454             return obj;
455         },
456
457         /**
458          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
459          * passed array is not really an array, your function is called once with it.
460          * The supplied function is called with (Object item, Number index, Array allItems).
461          * @param {Array/NodeList/Mixed} array
462          * @param {Function} fn
463          * @param {Object} scope
464          */
465         each : function(array, fn, scope){
466             if(typeof array.length == "undefined" || typeof array == "string"){
467                 array = [array];
468             }
469             for(var i = 0, len = array.length; i < len; i++){
470                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
471             }
472         },
473
474         // deprecated
475         combine : function(){
476             var as = arguments, l = as.length, r = [];
477             for(var i = 0; i < l; i++){
478                 var a = as[i];
479                 if(a instanceof Array){
480                     r = r.concat(a);
481                 }else if(a.length !== undefined && !a.substr){
482                     r = r.concat(Array.prototype.slice.call(a, 0));
483                 }else{
484                     r.push(a);
485                 }
486             }
487             return r;
488         },
489
490         /**
491          * Escapes the passed string for use in a regular expression
492          * @param {String} str
493          * @return {String}
494          */
495         escapeRe : function(s) {
496             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
497         },
498
499         // internal
500         callback : function(cb, scope, args, delay){
501             if(typeof cb == "function"){
502                 if(delay){
503                     cb.defer(delay, scope, args || []);
504                 }else{
505                     cb.apply(scope, args || []);
506                 }
507             }
508         },
509
510         /**
511          * Return the dom node for the passed string (id), dom node, or Roo.Element
512          * @param {String/HTMLElement/Roo.Element} el
513          * @return HTMLElement
514          */
515         getDom : function(el){
516             if(!el){
517                 return null;
518             }
519             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
520         },
521
522         /**
523         * Shorthand for {@link Roo.ComponentMgr#get}
524         * @param {String} id
525         * @return Roo.Component
526         */
527         getCmp : function(id){
528             return Roo.ComponentMgr.get(id);
529         },
530          
531         num : function(v, defaultValue){
532             if(typeof v != 'number'){
533                 return defaultValue;
534             }
535             return v;
536         },
537
538         destroy : function(){
539             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
540                 var as = a[i];
541                 if(as){
542                     if(as.dom){
543                         as.removeAllListeners();
544                         as.remove();
545                         continue;
546                     }
547                     if(typeof as.purgeListeners == 'function'){
548                         as.purgeListeners();
549                     }
550                     if(typeof as.destroy == 'function'){
551                         as.destroy();
552                     }
553                 }
554             }
555         },
556
557         // inpired by a similar function in mootools library
558         /**
559          * Returns the type of object that is passed in. If the object passed in is null or undefined it
560          * return false otherwise it returns one of the following values:<ul>
561          * <li><b>string</b>: If the object passed is a string</li>
562          * <li><b>number</b>: If the object passed is a number</li>
563          * <li><b>boolean</b>: If the object passed is a boolean value</li>
564          * <li><b>function</b>: If the object passed is a function reference</li>
565          * <li><b>object</b>: If the object passed is an object</li>
566          * <li><b>array</b>: If the object passed is an array</li>
567          * <li><b>regexp</b>: If the object passed is a regular expression</li>
568          * <li><b>element</b>: If the object passed is a DOM Element</li>
569          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
570          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
571          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
572          * @param {Mixed} object
573          * @return {String}
574          */
575         type : function(o){
576             if(o === undefined || o === null){
577                 return false;
578             }
579             if(o.htmlElement){
580                 return 'element';
581             }
582             var t = typeof o;
583             if(t == 'object' && o.nodeName) {
584                 switch(o.nodeType) {
585                     case 1: return 'element';
586                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
587                 }
588             }
589             if(t == 'object' || t == 'function') {
590                 switch(o.constructor) {
591                     case Array: return 'array';
592                     case RegExp: return 'regexp';
593                 }
594                 if(typeof o.length == 'number' && typeof o.item == 'function') {
595                     return 'nodelist';
596                 }
597             }
598             return t;
599         },
600
601         /**
602          * Returns true if the passed value is null, undefined or an empty string (optional).
603          * @param {Mixed} value The value to test
604          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
605          * @return {Boolean}
606          */
607         isEmpty : function(v, allowBlank){
608             return v === null || v === undefined || (!allowBlank ? v === '' : false);
609         },
610         
611         /** @type Boolean */
612         isOpera : isOpera,
613         /** @type Boolean */
614         isSafari : isSafari,
615         /** @type Boolean */
616         isFirefox : isFirefox,
617         /** @type Boolean */
618         isIE : isIE,
619         /** @type Boolean */
620         isIE7 : isIE7,
621         /** @type Boolean */
622         isIE11 : isIE11,
623         /** @type Boolean */
624         isGecko : isGecko,
625         /** @type Boolean */
626         isBorderBox : isBorderBox,
627         /** @type Boolean */
628         isWindows : isWindows,
629         /** @type Boolean */
630         isLinux : isLinux,
631         /** @type Boolean */
632         isMac : isMac,
633         /** @type Boolean */
634         isIOS : isIOS,
635         /** @type Boolean */
636         isTouch : isTouch,
637
638         /**
639          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
640          * you may want to set this to true.
641          * @type Boolean
642          */
643         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
644         
645         
646                 
647         /**
648          * Selects a single element as a Roo Element
649          * This is about as close as you can get to jQuery's $('do crazy stuff')
650          * @param {String} selector The selector/xpath query
651          * @param {Node} root (optional) The start of the query (defaults to document).
652          * @return {Roo.Element}
653          */
654         selectNode : function(selector, root) 
655         {
656             var node = Roo.DomQuery.selectNode(selector,root);
657             return node ? Roo.get(node) : new Roo.Element(false);
658         }
659         
660     });
661
662
663 })();
664
665 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
666                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
667                 "Roo.app", "Roo.ux",
668                 "Roo.bootstrap",
669                 "Roo.bootstrap.dash");
670 /*
671  * Based on:
672  * Ext JS Library 1.1.1
673  * Copyright(c) 2006-2007, Ext JS, LLC.
674  *
675  * Originally Released Under LGPL - original licence link has changed is not relivant.
676  *
677  * Fork - LGPL
678  * <script type="text/javascript">
679  */
680
681 (function() {    
682     // wrappedn so fnCleanup is not in global scope...
683     if(Roo.isIE) {
684         function fnCleanUp() {
685             var p = Function.prototype;
686             delete p.createSequence;
687             delete p.defer;
688             delete p.createDelegate;
689             delete p.createCallback;
690             delete p.createInterceptor;
691
692             window.detachEvent("onunload", fnCleanUp);
693         }
694         window.attachEvent("onunload", fnCleanUp);
695     }
696 })();
697
698
699 /**
700  * @class Function
701  * These functions are available on every Function object (any JavaScript function).
702  */
703 Roo.apply(Function.prototype, {
704      /**
705      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
706      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
707      * Will create a function that is bound to those 2 args.
708      * @return {Function} The new function
709     */
710     createCallback : function(/*args...*/){
711         // make args available, in function below
712         var args = arguments;
713         var method = this;
714         return function() {
715             return method.apply(window, args);
716         };
717     },
718
719     /**
720      * Creates a delegate (callback) that sets the scope to obj.
721      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
722      * Will create a function that is automatically scoped to this.
723      * @param {Object} obj (optional) The object for which the scope is set
724      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
725      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
726      *                                             if a number the args are inserted at the specified position
727      * @return {Function} The new function
728      */
729     createDelegate : function(obj, args, appendArgs){
730         var method = this;
731         return function() {
732             var callArgs = args || arguments;
733             if(appendArgs === true){
734                 callArgs = Array.prototype.slice.call(arguments, 0);
735                 callArgs = callArgs.concat(args);
736             }else if(typeof appendArgs == "number"){
737                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
738                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
739                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
740             }
741             return method.apply(obj || window, callArgs);
742         };
743     },
744
745     /**
746      * Calls this function after the number of millseconds specified.
747      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
748      * @param {Object} obj (optional) The object for which the scope is set
749      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
750      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
751      *                                             if a number the args are inserted at the specified position
752      * @return {Number} The timeout id that can be used with clearTimeout
753      */
754     defer : function(millis, obj, args, appendArgs){
755         var fn = this.createDelegate(obj, args, appendArgs);
756         if(millis){
757             return setTimeout(fn, millis);
758         }
759         fn();
760         return 0;
761     },
762     /**
763      * Create a combined function call sequence of the original function + the passed function.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function
766      * @param {Function} fcn The function to sequence
767      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
768      * @return {Function} The new function
769      */
770     createSequence : function(fcn, scope){
771         if(typeof fcn != "function"){
772             return this;
773         }
774         var method = this;
775         return function() {
776             var retval = method.apply(this || window, arguments);
777             fcn.apply(scope || this || window, arguments);
778             return retval;
779         };
780     },
781
782     /**
783      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
784      * The resulting function returns the results of the original function.
785      * The passed fcn is called with the parameters of the original function.
786      * @addon
787      * @param {Function} fcn The function to call before the original
788      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
789      * @return {Function} The new function
790      */
791     createInterceptor : function(fcn, scope){
792         if(typeof fcn != "function"){
793             return this;
794         }
795         var method = this;
796         return function() {
797             fcn.target = this;
798             fcn.method = method;
799             if(fcn.apply(scope || this || window, arguments) === false){
800                 return;
801             }
802             return method.apply(this || window, arguments);
803         };
804     }
805 });
806 /*
807  * Based on:
808  * Ext JS Library 1.1.1
809  * Copyright(c) 2006-2007, Ext JS, LLC.
810  *
811  * Originally Released Under LGPL - original licence link has changed is not relivant.
812  *
813  * Fork - LGPL
814  * <script type="text/javascript">
815  */
816
817 Roo.applyIf(String, {
818     
819     /** @scope String */
820     
821     /**
822      * Escapes the passed string for ' and \
823      * @param {String} string The string to escape
824      * @return {String} The escaped string
825      * @static
826      */
827     escape : function(string) {
828         return string.replace(/('|\\)/g, "\\$1");
829     },
830
831     /**
832      * Pads the left side of a string with a specified character.  This is especially useful
833      * for normalizing number and date strings.  Example usage:
834      * <pre><code>
835 var s = String.leftPad('123', 5, '0');
836 // s now contains the string: '00123'
837 </code></pre>
838      * @param {String} string The original string
839      * @param {Number} size The total length of the output string
840      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
841      * @return {String} The padded string
842      * @static
843      */
844     leftPad : function (val, size, ch) {
845         var result = new String(val);
846         if(ch === null || ch === undefined || ch === '') {
847             ch = " ";
848         }
849         while (result.length < size) {
850             result = ch + result;
851         }
852         return result;
853     },
854
855     /**
856      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
857      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
858      * <pre><code>
859 var cls = 'my-class', text = 'Some text';
860 var s = String.format('<div class="{0}">{1}</div>', cls, text);
861 // s now contains the string: '<div class="my-class">Some text</div>'
862 </code></pre>
863      * @param {String} string The tokenized string to be formatted
864      * @param {String} value1 The value to replace token {0}
865      * @param {String} value2 Etc...
866      * @return {String} The formatted string
867      * @static
868      */
869     format : function(format){
870         var args = Array.prototype.slice.call(arguments, 1);
871         return format.replace(/\{(\d+)\}/g, function(m, i){
872             return Roo.util.Format.htmlEncode(args[i]);
873         });
874     }
875 });
876
877 /**
878  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
879  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
880  * they are already different, the first value passed in is returned.  Note that this method returns the new value
881  * but does not change the current string.
882  * <pre><code>
883 // alternate sort directions
884 sort = sort.toggle('ASC', 'DESC');
885
886 // instead of conditional logic:
887 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
888 </code></pre>
889  * @param {String} value The value to compare to the current string
890  * @param {String} other The new value to use if the string already equals the first value passed in
891  * @return {String} The new value
892  */
893  
894 String.prototype.toggle = function(value, other){
895     return this == value ? other : value;
896 };/*
897  * Based on:
898  * Ext JS Library 1.1.1
899  * Copyright(c) 2006-2007, Ext JS, LLC.
900  *
901  * Originally Released Under LGPL - original licence link has changed is not relivant.
902  *
903  * Fork - LGPL
904  * <script type="text/javascript">
905  */
906
907  /**
908  * @class Number
909  */
910 Roo.applyIf(Number.prototype, {
911     /**
912      * Checks whether or not the current number is within a desired range.  If the number is already within the
913      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
914      * exceeded.  Note that this method returns the constrained value but does not change the current number.
915      * @param {Number} min The minimum number in the range
916      * @param {Number} max The maximum number in the range
917      * @return {Number} The constrained value if outside the range, otherwise the current value
918      */
919     constrain : function(min, max){
920         return Math.min(Math.max(this, min), max);
921     }
922 });/*
923  * Based on:
924  * Ext JS Library 1.1.1
925  * Copyright(c) 2006-2007, Ext JS, LLC.
926  *
927  * Originally Released Under LGPL - original licence link has changed is not relivant.
928  *
929  * Fork - LGPL
930  * <script type="text/javascript">
931  */
932  /**
933  * @class Array
934  */
935 Roo.applyIf(Array.prototype, {
936     /**
937      * 
938      * Checks whether or not the specified object exists in the array.
939      * @param {Object} o The object to check for
940      * @return {Number} The index of o in the array (or -1 if it is not found)
941      */
942     indexOf : function(o){
943        for (var i = 0, len = this.length; i < len; i++){
944               if(this[i] == o) return i;
945        }
946            return -1;
947     },
948
949     /**
950      * Removes the specified object from the array.  If the object is not found nothing happens.
951      * @param {Object} o The object to remove
952      */
953     remove : function(o){
954        var index = this.indexOf(o);
955        if(index != -1){
956            this.splice(index, 1);
957        }
958     },
959     /**
960      * Map (JS 1.6 compatibility)
961      * @param {Function} function  to call
962      */
963     map : function(fun )
964     {
965         var len = this.length >>> 0;
966         if (typeof fun != "function")
967             throw new TypeError();
968
969         var res = new Array(len);
970         var thisp = arguments[1];
971         for (var i = 0; i < len; i++)
972         {
973             if (i in this)
974                 res[i] = fun.call(thisp, this[i], i, this);
975         }
976
977         return res;
978     }
979     
980 });
981
982
983  /*
984  * Based on:
985  * Ext JS Library 1.1.1
986  * Copyright(c) 2006-2007, Ext JS, LLC.
987  *
988  * Originally Released Under LGPL - original licence link has changed is not relivant.
989  *
990  * Fork - LGPL
991  * <script type="text/javascript">
992  */
993
994 /**
995  * @class Date
996  *
997  * The date parsing and format syntax is a subset of
998  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
999  * supported will provide results equivalent to their PHP versions.
1000  *
1001  * Following is the list of all currently supported formats:
1002  *<pre>
1003 Sample date:
1004 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1005
1006 Format  Output      Description
1007 ------  ----------  --------------------------------------------------------------
1008   d      10         Day of the month, 2 digits with leading zeros
1009   D      Wed        A textual representation of a day, three letters
1010   j      10         Day of the month without leading zeros
1011   l      Wednesday  A full textual representation of the day of the week
1012   S      th         English ordinal day of month suffix, 2 chars (use with j)
1013   w      3          Numeric representation of the day of the week
1014   z      9          The julian date, or day of the year (0-365)
1015   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1016   F      January    A full textual representation of the month
1017   m      01         Numeric representation of a month, with leading zeros
1018   M      Jan        Month name abbreviation, three letters
1019   n      1          Numeric representation of a month, without leading zeros
1020   t      31         Number of days in the given month
1021   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1022   Y      2007       A full numeric representation of a year, 4 digits
1023   y      07         A two digit representation of a year
1024   a      pm         Lowercase Ante meridiem and Post meridiem
1025   A      PM         Uppercase Ante meridiem and Post meridiem
1026   g      3          12-hour format of an hour without leading zeros
1027   G      15         24-hour format of an hour without leading zeros
1028   h      03         12-hour format of an hour with leading zeros
1029   H      15         24-hour format of an hour with leading zeros
1030   i      05         Minutes with leading zeros
1031   s      01         Seconds, with leading zeros
1032   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1033   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1034   T      CST        Timezone setting of the machine running the code
1035   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1036 </pre>
1037  *
1038  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1039  * <pre><code>
1040 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1041 document.write(dt.format('Y-m-d'));                         //2007-01-10
1042 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1043 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
1044  </code></pre>
1045  *
1046  * Here are some standard date/time patterns that you might find helpful.  They
1047  * are not part of the source of Date.js, but to use them you can simply copy this
1048  * block of code into any script that is included after Date.js and they will also become
1049  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1050  * <pre><code>
1051 Date.patterns = {
1052     ISO8601Long:"Y-m-d H:i:s",
1053     ISO8601Short:"Y-m-d",
1054     ShortDate: "n/j/Y",
1055     LongDate: "l, F d, Y",
1056     FullDateTime: "l, F d, Y g:i:s A",
1057     MonthDay: "F d",
1058     ShortTime: "g:i A",
1059     LongTime: "g:i:s A",
1060     SortableDateTime: "Y-m-d\\TH:i:s",
1061     UniversalSortableDateTime: "Y-m-d H:i:sO",
1062     YearMonth: "F, Y"
1063 };
1064 </code></pre>
1065  *
1066  * Example usage:
1067  * <pre><code>
1068 var dt = new Date();
1069 document.write(dt.format(Date.patterns.ShortDate));
1070  </code></pre>
1071  */
1072
1073 /*
1074  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1075  * They generate precompiled functions from date formats instead of parsing and
1076  * processing the pattern every time you format a date.  These functions are available
1077  * on every Date object (any javascript function).
1078  *
1079  * The original article and download are here:
1080  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1081  *
1082  */
1083  
1084  
1085  // was in core
1086 /**
1087  Returns the number of milliseconds between this date and date
1088  @param {Date} date (optional) Defaults to now
1089  @return {Number} The diff in milliseconds
1090  @member Date getElapsed
1091  */
1092 Date.prototype.getElapsed = function(date) {
1093         return Math.abs((date || new Date()).getTime()-this.getTime());
1094 };
1095 // was in date file..
1096
1097
1098 // private
1099 Date.parseFunctions = {count:0};
1100 // private
1101 Date.parseRegexes = [];
1102 // private
1103 Date.formatFunctions = {count:0};
1104
1105 // private
1106 Date.prototype.dateFormat = function(format) {
1107     if (Date.formatFunctions[format] == null) {
1108         Date.createNewFormat(format);
1109     }
1110     var func = Date.formatFunctions[format];
1111     return this[func]();
1112 };
1113
1114
1115 /**
1116  * Formats a date given the supplied format string
1117  * @param {String} format The format string
1118  * @return {String} The formatted date
1119  * @method
1120  */
1121 Date.prototype.format = Date.prototype.dateFormat;
1122
1123 // private
1124 Date.createNewFormat = function(format) {
1125     var funcName = "format" + Date.formatFunctions.count++;
1126     Date.formatFunctions[format] = funcName;
1127     var code = "Date.prototype." + funcName + " = function(){return ";
1128     var special = false;
1129     var ch = '';
1130     for (var i = 0; i < format.length; ++i) {
1131         ch = format.charAt(i);
1132         if (!special && ch == "\\") {
1133             special = true;
1134         }
1135         else if (special) {
1136             special = false;
1137             code += "'" + String.escape(ch) + "' + ";
1138         }
1139         else {
1140             code += Date.getFormatCode(ch);
1141         }
1142     }
1143     /** eval:var:zzzzzzzzzzzzz */
1144     eval(code.substring(0, code.length - 3) + ";}");
1145 };
1146
1147 // private
1148 Date.getFormatCode = function(character) {
1149     switch (character) {
1150     case "d":
1151         return "String.leftPad(this.getDate(), 2, '0') + ";
1152     case "D":
1153         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1154     case "j":
1155         return "this.getDate() + ";
1156     case "l":
1157         return "Date.dayNames[this.getDay()] + ";
1158     case "S":
1159         return "this.getSuffix() + ";
1160     case "w":
1161         return "this.getDay() + ";
1162     case "z":
1163         return "this.getDayOfYear() + ";
1164     case "W":
1165         return "this.getWeekOfYear() + ";
1166     case "F":
1167         return "Date.monthNames[this.getMonth()] + ";
1168     case "m":
1169         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1170     case "M":
1171         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1172     case "n":
1173         return "(this.getMonth() + 1) + ";
1174     case "t":
1175         return "this.getDaysInMonth() + ";
1176     case "L":
1177         return "(this.isLeapYear() ? 1 : 0) + ";
1178     case "Y":
1179         return "this.getFullYear() + ";
1180     case "y":
1181         return "('' + this.getFullYear()).substring(2, 4) + ";
1182     case "a":
1183         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1184     case "A":
1185         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1186     case "g":
1187         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1188     case "G":
1189         return "this.getHours() + ";
1190     case "h":
1191         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1192     case "H":
1193         return "String.leftPad(this.getHours(), 2, '0') + ";
1194     case "i":
1195         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1196     case "s":
1197         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1198     case "O":
1199         return "this.getGMTOffset() + ";
1200     case "P":
1201         return "this.getGMTColonOffset() + ";
1202     case "T":
1203         return "this.getTimezone() + ";
1204     case "Z":
1205         return "(this.getTimezoneOffset() * -60) + ";
1206     default:
1207         return "'" + String.escape(character) + "' + ";
1208     }
1209 };
1210
1211 /**
1212  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1213  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1214  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1215  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1216  * string or the parse operation will fail.
1217  * Example Usage:
1218 <pre><code>
1219 //dt = Fri May 25 2007 (current date)
1220 var dt = new Date();
1221
1222 //dt = Thu May 25 2006 (today's month/day in 2006)
1223 dt = Date.parseDate("2006", "Y");
1224
1225 //dt = Sun Jan 15 2006 (all date parts specified)
1226 dt = Date.parseDate("2006-1-15", "Y-m-d");
1227
1228 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1229 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1230 </code></pre>
1231  * @param {String} input The unparsed date as a string
1232  * @param {String} format The format the date is in
1233  * @return {Date} The parsed date
1234  * @static
1235  */
1236 Date.parseDate = function(input, format) {
1237     if (Date.parseFunctions[format] == null) {
1238         Date.createParser(format);
1239     }
1240     var func = Date.parseFunctions[format];
1241     return Date[func](input);
1242 };
1243 /**
1244  * @private
1245  */
1246
1247 Date.createParser = function(format) {
1248     var funcName = "parse" + Date.parseFunctions.count++;
1249     var regexNum = Date.parseRegexes.length;
1250     var currentGroup = 1;
1251     Date.parseFunctions[format] = funcName;
1252
1253     var code = "Date." + funcName + " = function(input){\n"
1254         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1255         + "var d = new Date();\n"
1256         + "y = d.getFullYear();\n"
1257         + "m = d.getMonth();\n"
1258         + "d = d.getDate();\n"
1259         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1260         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1261         + "if (results && results.length > 0) {";
1262     var regex = "";
1263
1264     var special = false;
1265     var ch = '';
1266     for (var i = 0; i < format.length; ++i) {
1267         ch = format.charAt(i);
1268         if (!special && ch == "\\") {
1269             special = true;
1270         }
1271         else if (special) {
1272             special = false;
1273             regex += String.escape(ch);
1274         }
1275         else {
1276             var obj = Date.formatCodeToRegex(ch, currentGroup);
1277             currentGroup += obj.g;
1278             regex += obj.s;
1279             if (obj.g && obj.c) {
1280                 code += obj.c;
1281             }
1282         }
1283     }
1284
1285     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1286         + "{v = new Date(y, m, d, h, i, s);}\n"
1287         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1288         + "{v = new Date(y, m, d, h, i);}\n"
1289         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1290         + "{v = new Date(y, m, d, h);}\n"
1291         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1292         + "{v = new Date(y, m, d);}\n"
1293         + "else if (y >= 0 && m >= 0)\n"
1294         + "{v = new Date(y, m);}\n"
1295         + "else if (y >= 0)\n"
1296         + "{v = new Date(y);}\n"
1297         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1298         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1299         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1300         + ";}";
1301
1302     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1303     /** eval:var:zzzzzzzzzzzzz */
1304     eval(code);
1305 };
1306
1307 // private
1308 Date.formatCodeToRegex = function(character, currentGroup) {
1309     switch (character) {
1310     case "D":
1311         return {g:0,
1312         c:null,
1313         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1314     case "j":
1315         return {g:1,
1316             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1317             s:"(\\d{1,2})"}; // day of month without leading zeroes
1318     case "d":
1319         return {g:1,
1320             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1321             s:"(\\d{2})"}; // day of month with leading zeroes
1322     case "l":
1323         return {g:0,
1324             c:null,
1325             s:"(?:" + Date.dayNames.join("|") + ")"};
1326     case "S":
1327         return {g:0,
1328             c:null,
1329             s:"(?:st|nd|rd|th)"};
1330     case "w":
1331         return {g:0,
1332             c:null,
1333             s:"\\d"};
1334     case "z":
1335         return {g:0,
1336             c:null,
1337             s:"(?:\\d{1,3})"};
1338     case "W":
1339         return {g:0,
1340             c:null,
1341             s:"(?:\\d{2})"};
1342     case "F":
1343         return {g:1,
1344             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1345             s:"(" + Date.monthNames.join("|") + ")"};
1346     case "M":
1347         return {g:1,
1348             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1349             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1350     case "n":
1351         return {g:1,
1352             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1353             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1354     case "m":
1355         return {g:1,
1356             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1357             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1358     case "t":
1359         return {g:0,
1360             c:null,
1361             s:"\\d{1,2}"};
1362     case "L":
1363         return {g:0,
1364             c:null,
1365             s:"(?:1|0)"};
1366     case "Y":
1367         return {g:1,
1368             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1369             s:"(\\d{4})"};
1370     case "y":
1371         return {g:1,
1372             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1373                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1374             s:"(\\d{1,2})"};
1375     case "a":
1376         return {g:1,
1377             c:"if (results[" + currentGroup + "] == 'am') {\n"
1378                 + "if (h == 12) { h = 0; }\n"
1379                 + "} else { if (h < 12) { h += 12; }}",
1380             s:"(am|pm)"};
1381     case "A":
1382         return {g:1,
1383             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1384                 + "if (h == 12) { h = 0; }\n"
1385                 + "} else { if (h < 12) { h += 12; }}",
1386             s:"(AM|PM)"};
1387     case "g":
1388     case "G":
1389         return {g:1,
1390             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1391             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1392     case "h":
1393     case "H":
1394         return {g:1,
1395             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1396             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1397     case "i":
1398         return {g:1,
1399             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1400             s:"(\\d{2})"};
1401     case "s":
1402         return {g:1,
1403             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1404             s:"(\\d{2})"};
1405     case "O":
1406         return {g:1,
1407             c:[
1408                 "o = results[", currentGroup, "];\n",
1409                 "var sn = o.substring(0,1);\n", // get + / - sign
1410                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1411                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1412                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1413                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1414             ].join(""),
1415             s:"([+\-]\\d{2,4})"};
1416     
1417     
1418     case "P":
1419         return {g:1,
1420                 c:[
1421                    "o = results[", currentGroup, "];\n",
1422                    "var sn = o.substring(0,1);\n",
1423                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1424                    "var mn = o.substring(4,6) % 60;\n",
1425                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1426                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1427             ].join(""),
1428             s:"([+\-]\\d{4})"};
1429     case "T":
1430         return {g:0,
1431             c:null,
1432             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1433     case "Z":
1434         return {g:1,
1435             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1436                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1437             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1438     default:
1439         return {g:0,
1440             c:null,
1441             s:String.escape(character)};
1442     }
1443 };
1444
1445 /**
1446  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1447  * @return {String} The abbreviated timezone name (e.g. 'CST')
1448  */
1449 Date.prototype.getTimezone = function() {
1450     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1451 };
1452
1453 /**
1454  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1455  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1456  */
1457 Date.prototype.getGMTOffset = function() {
1458     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1459         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1460         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1461 };
1462
1463 /**
1464  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1465  * @return {String} 2-characters representing hours and 2-characters representing minutes
1466  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1467  */
1468 Date.prototype.getGMTColonOffset = function() {
1469         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1470                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1471                 + ":"
1472                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1473 }
1474
1475 /**
1476  * Get the numeric day number of the year, adjusted for leap year.
1477  * @return {Number} 0 through 364 (365 in leap years)
1478  */
1479 Date.prototype.getDayOfYear = function() {
1480     var num = 0;
1481     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1482     for (var i = 0; i < this.getMonth(); ++i) {
1483         num += Date.daysInMonth[i];
1484     }
1485     return num + this.getDate() - 1;
1486 };
1487
1488 /**
1489  * Get the string representation of the numeric week number of the year
1490  * (equivalent to the format specifier 'W').
1491  * @return {String} '00' through '52'
1492  */
1493 Date.prototype.getWeekOfYear = function() {
1494     // Skip to Thursday of this week
1495     var now = this.getDayOfYear() + (4 - this.getDay());
1496     // Find the first Thursday of the year
1497     var jan1 = new Date(this.getFullYear(), 0, 1);
1498     var then = (7 - jan1.getDay() + 4);
1499     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1500 };
1501
1502 /**
1503  * Whether or not the current date is in a leap year.
1504  * @return {Boolean} True if the current date is in a leap year, else false
1505  */
1506 Date.prototype.isLeapYear = function() {
1507     var year = this.getFullYear();
1508     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1509 };
1510
1511 /**
1512  * Get the first day of the current month, adjusted for leap year.  The returned value
1513  * is the numeric day index within the week (0-6) which can be used in conjunction with
1514  * the {@link #monthNames} array to retrieve the textual day name.
1515  * Example:
1516  *<pre><code>
1517 var dt = new Date('1/10/2007');
1518 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1519 </code></pre>
1520  * @return {Number} The day number (0-6)
1521  */
1522 Date.prototype.getFirstDayOfMonth = function() {
1523     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1524     return (day < 0) ? (day + 7) : day;
1525 };
1526
1527 /**
1528  * Get the last day of the current month, adjusted for leap year.  The returned value
1529  * is the numeric day index within the week (0-6) which can be used in conjunction with
1530  * the {@link #monthNames} array to retrieve the textual day name.
1531  * Example:
1532  *<pre><code>
1533 var dt = new Date('1/10/2007');
1534 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1535 </code></pre>
1536  * @return {Number} The day number (0-6)
1537  */
1538 Date.prototype.getLastDayOfMonth = function() {
1539     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1540     return (day < 0) ? (day + 7) : day;
1541 };
1542
1543
1544 /**
1545  * Get the first date of this date's month
1546  * @return {Date}
1547  */
1548 Date.prototype.getFirstDateOfMonth = function() {
1549     return new Date(this.getFullYear(), this.getMonth(), 1);
1550 };
1551
1552 /**
1553  * Get the last date of this date's month
1554  * @return {Date}
1555  */
1556 Date.prototype.getLastDateOfMonth = function() {
1557     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1558 };
1559 /**
1560  * Get the number of days in the current month, adjusted for leap year.
1561  * @return {Number} The number of days in the month
1562  */
1563 Date.prototype.getDaysInMonth = function() {
1564     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1565     return Date.daysInMonth[this.getMonth()];
1566 };
1567
1568 /**
1569  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1570  * @return {String} 'st, 'nd', 'rd' or 'th'
1571  */
1572 Date.prototype.getSuffix = function() {
1573     switch (this.getDate()) {
1574         case 1:
1575         case 21:
1576         case 31:
1577             return "st";
1578         case 2:
1579         case 22:
1580             return "nd";
1581         case 3:
1582         case 23:
1583             return "rd";
1584         default:
1585             return "th";
1586     }
1587 };
1588
1589 // private
1590 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1591
1592 /**
1593  * An array of textual month names.
1594  * Override these values for international dates, for example...
1595  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1596  * @type Array
1597  * @static
1598  */
1599 Date.monthNames =
1600    ["January",
1601     "February",
1602     "March",
1603     "April",
1604     "May",
1605     "June",
1606     "July",
1607     "August",
1608     "September",
1609     "October",
1610     "November",
1611     "December"];
1612
1613 /**
1614  * An array of textual day names.
1615  * Override these values for international dates, for example...
1616  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1617  * @type Array
1618  * @static
1619  */
1620 Date.dayNames =
1621    ["Sunday",
1622     "Monday",
1623     "Tuesday",
1624     "Wednesday",
1625     "Thursday",
1626     "Friday",
1627     "Saturday"];
1628
1629 // private
1630 Date.y2kYear = 50;
1631 // private
1632 Date.monthNumbers = {
1633     Jan:0,
1634     Feb:1,
1635     Mar:2,
1636     Apr:3,
1637     May:4,
1638     Jun:5,
1639     Jul:6,
1640     Aug:7,
1641     Sep:8,
1642     Oct:9,
1643     Nov:10,
1644     Dec:11};
1645
1646 /**
1647  * Creates and returns a new Date instance with the exact same date value as the called instance.
1648  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1649  * variable will also be changed.  When the intention is to create a new variable that will not
1650  * modify the original instance, you should create a clone.
1651  *
1652  * Example of correctly cloning a date:
1653  * <pre><code>
1654 //wrong way:
1655 var orig = new Date('10/1/2006');
1656 var copy = orig;
1657 copy.setDate(5);
1658 document.write(orig);  //returns 'Thu Oct 05 2006'!
1659
1660 //correct way:
1661 var orig = new Date('10/1/2006');
1662 var copy = orig.clone();
1663 copy.setDate(5);
1664 document.write(orig);  //returns 'Thu Oct 01 2006'
1665 </code></pre>
1666  * @return {Date} The new Date instance
1667  */
1668 Date.prototype.clone = function() {
1669         return new Date(this.getTime());
1670 };
1671
1672 /**
1673  * Clears any time information from this date
1674  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1675  @return {Date} this or the clone
1676  */
1677 Date.prototype.clearTime = function(clone){
1678     if(clone){
1679         return this.clone().clearTime();
1680     }
1681     this.setHours(0);
1682     this.setMinutes(0);
1683     this.setSeconds(0);
1684     this.setMilliseconds(0);
1685     return this;
1686 };
1687
1688 // private
1689 // safari setMonth is broken
1690 if(Roo.isSafari){
1691     Date.brokenSetMonth = Date.prototype.setMonth;
1692         Date.prototype.setMonth = function(num){
1693                 if(num <= -1){
1694                         var n = Math.ceil(-num);
1695                         var back_year = Math.ceil(n/12);
1696                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1697                         this.setFullYear(this.getFullYear() - back_year);
1698                         return Date.brokenSetMonth.call(this, month);
1699                 } else {
1700                         return Date.brokenSetMonth.apply(this, arguments);
1701                 }
1702         };
1703 }
1704
1705 /** Date interval constant 
1706 * @static 
1707 * @type String */
1708 Date.MILLI = "ms";
1709 /** Date interval constant 
1710 * @static 
1711 * @type String */
1712 Date.SECOND = "s";
1713 /** Date interval constant 
1714 * @static 
1715 * @type String */
1716 Date.MINUTE = "mi";
1717 /** Date interval constant 
1718 * @static 
1719 * @type String */
1720 Date.HOUR = "h";
1721 /** Date interval constant 
1722 * @static 
1723 * @type String */
1724 Date.DAY = "d";
1725 /** Date interval constant 
1726 * @static 
1727 * @type String */
1728 Date.MONTH = "mo";
1729 /** Date interval constant 
1730 * @static 
1731 * @type String */
1732 Date.YEAR = "y";
1733
1734 /**
1735  * Provides a convenient method of performing basic date arithmetic.  This method
1736  * does not modify the Date instance being called - it creates and returns
1737  * a new Date instance containing the resulting date value.
1738  *
1739  * Examples:
1740  * <pre><code>
1741 //Basic usage:
1742 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1743 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1744
1745 //Negative values will subtract correctly:
1746 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1747 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1748
1749 //You can even chain several calls together in one line!
1750 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1751 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1752  </code></pre>
1753  *
1754  * @param {String} interval   A valid date interval enum value
1755  * @param {Number} value      The amount to add to the current date
1756  * @return {Date} The new Date instance
1757  */
1758 Date.prototype.add = function(interval, value){
1759   var d = this.clone();
1760   if (!interval || value === 0) return d;
1761   switch(interval.toLowerCase()){
1762     case Date.MILLI:
1763       d.setMilliseconds(this.getMilliseconds() + value);
1764       break;
1765     case Date.SECOND:
1766       d.setSeconds(this.getSeconds() + value);
1767       break;
1768     case Date.MINUTE:
1769       d.setMinutes(this.getMinutes() + value);
1770       break;
1771     case Date.HOUR:
1772       d.setHours(this.getHours() + value);
1773       break;
1774     case Date.DAY:
1775       d.setDate(this.getDate() + value);
1776       break;
1777     case Date.MONTH:
1778       var day = this.getDate();
1779       if(day > 28){
1780           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1781       }
1782       d.setDate(day);
1783       d.setMonth(this.getMonth() + value);
1784       break;
1785     case Date.YEAR:
1786       d.setFullYear(this.getFullYear() + value);
1787       break;
1788   }
1789   return d;
1790 };
1791 /*
1792  * Based on:
1793  * Ext JS Library 1.1.1
1794  * Copyright(c) 2006-2007, Ext JS, LLC.
1795  *
1796  * Originally Released Under LGPL - original licence link has changed is not relivant.
1797  *
1798  * Fork - LGPL
1799  * <script type="text/javascript">
1800  */
1801
1802 /**
1803  * @class Roo.lib.Dom
1804  * @static
1805  * 
1806  * Dom utils (from YIU afaik)
1807  * 
1808  **/
1809 Roo.lib.Dom = {
1810     /**
1811      * Get the view width
1812      * @param {Boolean} full True will get the full document, otherwise it's the view width
1813      * @return {Number} The width
1814      */
1815      
1816     getViewWidth : function(full) {
1817         return full ? this.getDocumentWidth() : this.getViewportWidth();
1818     },
1819     /**
1820      * Get the view height
1821      * @param {Boolean} full True will get the full document, otherwise it's the view height
1822      * @return {Number} The height
1823      */
1824     getViewHeight : function(full) {
1825         return full ? this.getDocumentHeight() : this.getViewportHeight();
1826     },
1827
1828     getDocumentHeight: function() {
1829         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1830         return Math.max(scrollHeight, this.getViewportHeight());
1831     },
1832
1833     getDocumentWidth: function() {
1834         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1835         return Math.max(scrollWidth, this.getViewportWidth());
1836     },
1837
1838     getViewportHeight: function() {
1839         var height = self.innerHeight;
1840         var mode = document.compatMode;
1841
1842         if ((mode || Roo.isIE) && !Roo.isOpera) {
1843             height = (mode == "CSS1Compat") ?
1844                      document.documentElement.clientHeight :
1845                      document.body.clientHeight;
1846         }
1847
1848         return height;
1849     },
1850
1851     getViewportWidth: function() {
1852         var width = self.innerWidth;
1853         var mode = document.compatMode;
1854
1855         if (mode || Roo.isIE) {
1856             width = (mode == "CSS1Compat") ?
1857                     document.documentElement.clientWidth :
1858                     document.body.clientWidth;
1859         }
1860         return width;
1861     },
1862
1863     isAncestor : function(p, c) {
1864         p = Roo.getDom(p);
1865         c = Roo.getDom(c);
1866         if (!p || !c) {
1867             return false;
1868         }
1869
1870         if (p.contains && !Roo.isSafari) {
1871             return p.contains(c);
1872         } else if (p.compareDocumentPosition) {
1873             return !!(p.compareDocumentPosition(c) & 16);
1874         } else {
1875             var parent = c.parentNode;
1876             while (parent) {
1877                 if (parent == p) {
1878                     return true;
1879                 }
1880                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1881                     return false;
1882                 }
1883                 parent = parent.parentNode;
1884             }
1885             return false;
1886         }
1887     },
1888
1889     getRegion : function(el) {
1890         return Roo.lib.Region.getRegion(el);
1891     },
1892
1893     getY : function(el) {
1894         return this.getXY(el)[1];
1895     },
1896
1897     getX : function(el) {
1898         return this.getXY(el)[0];
1899     },
1900
1901     getXY : function(el) {
1902         var p, pe, b, scroll, bd = document.body;
1903         el = Roo.getDom(el);
1904         var fly = Roo.lib.AnimBase.fly;
1905         if (el.getBoundingClientRect) {
1906             b = el.getBoundingClientRect();
1907             scroll = fly(document).getScroll();
1908             return [b.left + scroll.left, b.top + scroll.top];
1909         }
1910         var x = 0, y = 0;
1911
1912         p = el;
1913
1914         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1915
1916         while (p) {
1917
1918             x += p.offsetLeft;
1919             y += p.offsetTop;
1920
1921             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1922                 hasAbsolute = true;
1923             }
1924
1925             if (Roo.isGecko) {
1926                 pe = fly(p);
1927
1928                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1929                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1930
1931
1932                 x += bl;
1933                 y += bt;
1934
1935
1936                 if (p != el && pe.getStyle('overflow') != 'visible') {
1937                     x += bl;
1938                     y += bt;
1939                 }
1940             }
1941             p = p.offsetParent;
1942         }
1943
1944         if (Roo.isSafari && hasAbsolute) {
1945             x -= bd.offsetLeft;
1946             y -= bd.offsetTop;
1947         }
1948
1949         if (Roo.isGecko && !hasAbsolute) {
1950             var dbd = fly(bd);
1951             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1952             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1953         }
1954
1955         p = el.parentNode;
1956         while (p && p != bd) {
1957             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1958                 x -= p.scrollLeft;
1959                 y -= p.scrollTop;
1960             }
1961             p = p.parentNode;
1962         }
1963         return [x, y];
1964     },
1965  
1966   
1967
1968
1969     setXY : function(el, xy) {
1970         el = Roo.fly(el, '_setXY');
1971         el.position();
1972         var pts = el.translatePoints(xy);
1973         if (xy[0] !== false) {
1974             el.dom.style.left = pts.left + "px";
1975         }
1976         if (xy[1] !== false) {
1977             el.dom.style.top = pts.top + "px";
1978         }
1979     },
1980
1981     setX : function(el, x) {
1982         this.setXY(el, [x, false]);
1983     },
1984
1985     setY : function(el, y) {
1986         this.setXY(el, [false, y]);
1987     }
1988 };
1989 /*
1990  * Portions of this file are based on pieces of Yahoo User Interface Library
1991  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1992  * YUI licensed under the BSD License:
1993  * http://developer.yahoo.net/yui/license.txt
1994  * <script type="text/javascript">
1995  *
1996  */
1997
1998 Roo.lib.Event = function() {
1999     var loadComplete = false;
2000     var listeners = [];
2001     var unloadListeners = [];
2002     var retryCount = 0;
2003     var onAvailStack = [];
2004     var counter = 0;
2005     var lastError = null;
2006
2007     return {
2008         POLL_RETRYS: 200,
2009         POLL_INTERVAL: 20,
2010         EL: 0,
2011         TYPE: 1,
2012         FN: 2,
2013         WFN: 3,
2014         OBJ: 3,
2015         ADJ_SCOPE: 4,
2016         _interval: null,
2017
2018         startInterval: function() {
2019             if (!this._interval) {
2020                 var self = this;
2021                 var callback = function() {
2022                     self._tryPreloadAttach();
2023                 };
2024                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2025
2026             }
2027         },
2028
2029         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2030             onAvailStack.push({ id:         p_id,
2031                 fn:         p_fn,
2032                 obj:        p_obj,
2033                 override:   p_override,
2034                 checkReady: false    });
2035
2036             retryCount = this.POLL_RETRYS;
2037             this.startInterval();
2038         },
2039
2040
2041         addListener: function(el, eventName, fn) {
2042             el = Roo.getDom(el);
2043             if (!el || !fn) {
2044                 return false;
2045             }
2046
2047             if ("unload" == eventName) {
2048                 unloadListeners[unloadListeners.length] =
2049                 [el, eventName, fn];
2050                 return true;
2051             }
2052
2053             var wrappedFn = function(e) {
2054                 return fn(Roo.lib.Event.getEvent(e));
2055             };
2056
2057             var li = [el, eventName, fn, wrappedFn];
2058
2059             var index = listeners.length;
2060             listeners[index] = li;
2061
2062             this.doAdd(el, eventName, wrappedFn, false);
2063             return true;
2064
2065         },
2066
2067
2068         removeListener: function(el, eventName, fn) {
2069             var i, len;
2070
2071             el = Roo.getDom(el);
2072
2073             if(!fn) {
2074                 return this.purgeElement(el, false, eventName);
2075             }
2076
2077
2078             if ("unload" == eventName) {
2079
2080                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2081                     var li = unloadListeners[i];
2082                     if (li &&
2083                         li[0] == el &&
2084                         li[1] == eventName &&
2085                         li[2] == fn) {
2086                         unloadListeners.splice(i, 1);
2087                         return true;
2088                     }
2089                 }
2090
2091                 return false;
2092             }
2093
2094             var cacheItem = null;
2095
2096
2097             var index = arguments[3];
2098
2099             if ("undefined" == typeof index) {
2100                 index = this._getCacheIndex(el, eventName, fn);
2101             }
2102
2103             if (index >= 0) {
2104                 cacheItem = listeners[index];
2105             }
2106
2107             if (!el || !cacheItem) {
2108                 return false;
2109             }
2110
2111             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2112
2113             delete listeners[index][this.WFN];
2114             delete listeners[index][this.FN];
2115             listeners.splice(index, 1);
2116
2117             return true;
2118
2119         },
2120
2121
2122         getTarget: function(ev, resolveTextNode) {
2123             ev = ev.browserEvent || ev;
2124             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2125             var t = ev.target || ev.srcElement;
2126             return this.resolveTextNode(t);
2127         },
2128
2129
2130         resolveTextNode: function(node) {
2131             if (Roo.isSafari && node && 3 == node.nodeType) {
2132                 return node.parentNode;
2133             } else {
2134                 return node;
2135             }
2136         },
2137
2138
2139         getPageX: function(ev) {
2140             ev = ev.browserEvent || ev;
2141             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2142             var x = ev.pageX;
2143             if (!x && 0 !== x) {
2144                 x = ev.clientX || 0;
2145
2146                 if (Roo.isIE) {
2147                     x += this.getScroll()[1];
2148                 }
2149             }
2150
2151             return x;
2152         },
2153
2154
2155         getPageY: function(ev) {
2156             ev = ev.browserEvent || ev;
2157             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2158             var y = ev.pageY;
2159             if (!y && 0 !== y) {
2160                 y = ev.clientY || 0;
2161
2162                 if (Roo.isIE) {
2163                     y += this.getScroll()[0];
2164                 }
2165             }
2166
2167
2168             return y;
2169         },
2170
2171
2172         getXY: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             return [this.getPageX(ev), this.getPageY(ev)];
2176         },
2177
2178
2179         getRelatedTarget: function(ev) {
2180             ev = ev.browserEvent || ev;
2181             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2182             var t = ev.relatedTarget;
2183             if (!t) {
2184                 if (ev.type == "mouseout") {
2185                     t = ev.toElement;
2186                 } else if (ev.type == "mouseover") {
2187                     t = ev.fromElement;
2188                 }
2189             }
2190
2191             return this.resolveTextNode(t);
2192         },
2193
2194
2195         getTime: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2198             if (!ev.time) {
2199                 var t = new Date().getTime();
2200                 try {
2201                     ev.time = t;
2202                 } catch(ex) {
2203                     this.lastError = ex;
2204                     return t;
2205                 }
2206             }
2207
2208             return ev.time;
2209         },
2210
2211
2212         stopEvent: function(ev) {
2213             this.stopPropagation(ev);
2214             this.preventDefault(ev);
2215         },
2216
2217
2218         stopPropagation: function(ev) {
2219             ev = ev.browserEvent || ev;
2220             if (ev.stopPropagation) {
2221                 ev.stopPropagation();
2222             } else {
2223                 ev.cancelBubble = true;
2224             }
2225         },
2226
2227
2228         preventDefault: function(ev) {
2229             ev = ev.browserEvent || ev;
2230             if(ev.preventDefault) {
2231                 ev.preventDefault();
2232             } else {
2233                 ev.returnValue = false;
2234             }
2235         },
2236
2237
2238         getEvent: function(e) {
2239             var ev = e || window.event;
2240             if (!ev) {
2241                 var c = this.getEvent.caller;
2242                 while (c) {
2243                     ev = c.arguments[0];
2244                     if (ev && Event == ev.constructor) {
2245                         break;
2246                     }
2247                     c = c.caller;
2248                 }
2249             }
2250             return ev;
2251         },
2252
2253
2254         getCharCode: function(ev) {
2255             ev = ev.browserEvent || ev;
2256             return ev.charCode || ev.keyCode || 0;
2257         },
2258
2259
2260         _getCacheIndex: function(el, eventName, fn) {
2261             for (var i = 0,len = listeners.length; i < len; ++i) {
2262                 var li = listeners[i];
2263                 if (li &&
2264                     li[this.FN] == fn &&
2265                     li[this.EL] == el &&
2266                     li[this.TYPE] == eventName) {
2267                     return i;
2268                 }
2269             }
2270
2271             return -1;
2272         },
2273
2274
2275         elCache: {},
2276
2277
2278         getEl: function(id) {
2279             return document.getElementById(id);
2280         },
2281
2282
2283         clearCache: function() {
2284         },
2285
2286
2287         _load: function(e) {
2288             loadComplete = true;
2289             var EU = Roo.lib.Event;
2290
2291
2292             if (Roo.isIE) {
2293                 EU.doRemove(window, "load", EU._load);
2294             }
2295         },
2296
2297
2298         _tryPreloadAttach: function() {
2299
2300             if (this.locked) {
2301                 return false;
2302             }
2303
2304             this.locked = true;
2305
2306
2307             var tryAgain = !loadComplete;
2308             if (!tryAgain) {
2309                 tryAgain = (retryCount > 0);
2310             }
2311
2312
2313             var notAvail = [];
2314             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2315                 var item = onAvailStack[i];
2316                 if (item) {
2317                     var el = this.getEl(item.id);
2318
2319                     if (el) {
2320                         if (!item.checkReady ||
2321                             loadComplete ||
2322                             el.nextSibling ||
2323                             (document && document.body)) {
2324
2325                             var scope = el;
2326                             if (item.override) {
2327                                 if (item.override === true) {
2328                                     scope = item.obj;
2329                                 } else {
2330                                     scope = item.override;
2331                                 }
2332                             }
2333                             item.fn.call(scope, item.obj);
2334                             onAvailStack[i] = null;
2335                         }
2336                     } else {
2337                         notAvail.push(item);
2338                     }
2339                 }
2340             }
2341
2342             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2343
2344             if (tryAgain) {
2345
2346                 this.startInterval();
2347             } else {
2348                 clearInterval(this._interval);
2349                 this._interval = null;
2350             }
2351
2352             this.locked = false;
2353
2354             return true;
2355
2356         },
2357
2358
2359         purgeElement: function(el, recurse, eventName) {
2360             var elListeners = this.getListeners(el, eventName);
2361             if (elListeners) {
2362                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2363                     var l = elListeners[i];
2364                     this.removeListener(el, l.type, l.fn);
2365                 }
2366             }
2367
2368             if (recurse && el && el.childNodes) {
2369                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2370                     this.purgeElement(el.childNodes[i], recurse, eventName);
2371                 }
2372             }
2373         },
2374
2375
2376         getListeners: function(el, eventName) {
2377             var results = [], searchLists;
2378             if (!eventName) {
2379                 searchLists = [listeners, unloadListeners];
2380             } else if (eventName == "unload") {
2381                 searchLists = [unloadListeners];
2382             } else {
2383                 searchLists = [listeners];
2384             }
2385
2386             for (var j = 0; j < searchLists.length; ++j) {
2387                 var searchList = searchLists[j];
2388                 if (searchList && searchList.length > 0) {
2389                     for (var i = 0,len = searchList.length; i < len; ++i) {
2390                         var l = searchList[i];
2391                         if (l && l[this.EL] === el &&
2392                             (!eventName || eventName === l[this.TYPE])) {
2393                             results.push({
2394                                 type:   l[this.TYPE],
2395                                 fn:     l[this.FN],
2396                                 obj:    l[this.OBJ],
2397                                 adjust: l[this.ADJ_SCOPE],
2398                                 index:  i
2399                             });
2400                         }
2401                     }
2402                 }
2403             }
2404
2405             return (results.length) ? results : null;
2406         },
2407
2408
2409         _unload: function(e) {
2410
2411             var EU = Roo.lib.Event, i, j, l, len, index;
2412
2413             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2414                 l = unloadListeners[i];
2415                 if (l) {
2416                     var scope = window;
2417                     if (l[EU.ADJ_SCOPE]) {
2418                         if (l[EU.ADJ_SCOPE] === true) {
2419                             scope = l[EU.OBJ];
2420                         } else {
2421                             scope = l[EU.ADJ_SCOPE];
2422                         }
2423                     }
2424                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2425                     unloadListeners[i] = null;
2426                     l = null;
2427                     scope = null;
2428                 }
2429             }
2430
2431             unloadListeners = null;
2432
2433             if (listeners && listeners.length > 0) {
2434                 j = listeners.length;
2435                 while (j) {
2436                     index = j - 1;
2437                     l = listeners[index];
2438                     if (l) {
2439                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2440                                 l[EU.FN], index);
2441                     }
2442                     j = j - 1;
2443                 }
2444                 l = null;
2445
2446                 EU.clearCache();
2447             }
2448
2449             EU.doRemove(window, "unload", EU._unload);
2450
2451         },
2452
2453
2454         getScroll: function() {
2455             var dd = document.documentElement, db = document.body;
2456             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2457                 return [dd.scrollTop, dd.scrollLeft];
2458             } else if (db) {
2459                 return [db.scrollTop, db.scrollLeft];
2460             } else {
2461                 return [0, 0];
2462             }
2463         },
2464
2465
2466         doAdd: function () {
2467             if (window.addEventListener) {
2468                 return function(el, eventName, fn, capture) {
2469                     el.addEventListener(eventName, fn, (capture));
2470                 };
2471             } else if (window.attachEvent) {
2472                 return function(el, eventName, fn, capture) {
2473                     el.attachEvent("on" + eventName, fn);
2474                 };
2475             } else {
2476                 return function() {
2477                 };
2478             }
2479         }(),
2480
2481
2482         doRemove: function() {
2483             if (window.removeEventListener) {
2484                 return function (el, eventName, fn, capture) {
2485                     el.removeEventListener(eventName, fn, (capture));
2486                 };
2487             } else if (window.detachEvent) {
2488                 return function (el, eventName, fn) {
2489                     el.detachEvent("on" + eventName, fn);
2490                 };
2491             } else {
2492                 return function() {
2493                 };
2494             }
2495         }()
2496     };
2497     
2498 }();
2499 (function() {     
2500    
2501     var E = Roo.lib.Event;
2502     E.on = E.addListener;
2503     E.un = E.removeListener;
2504
2505     if (document && document.body) {
2506         E._load();
2507     } else {
2508         E.doAdd(window, "load", E._load);
2509     }
2510     E.doAdd(window, "unload", E._unload);
2511     E._tryPreloadAttach();
2512 })();
2513
2514 /*
2515  * Portions of this file are based on pieces of Yahoo User Interface Library
2516  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2517  * YUI licensed under the BSD License:
2518  * http://developer.yahoo.net/yui/license.txt
2519  * <script type="text/javascript">
2520  *
2521  */
2522
2523 (function() {
2524     /**
2525      * @class Roo.lib.Ajax
2526      *
2527      */
2528     Roo.lib.Ajax = {
2529         /**
2530          * @static 
2531          */
2532         request : function(method, uri, cb, data, options) {
2533             if(options){
2534                 var hs = options.headers;
2535                 if(hs){
2536                     for(var h in hs){
2537                         if(hs.hasOwnProperty(h)){
2538                             this.initHeader(h, hs[h], false);
2539                         }
2540                     }
2541                 }
2542                 if(options.xmlData){
2543                     this.initHeader('Content-Type', 'text/xml', false);
2544                     method = 'POST';
2545                     data = options.xmlData;
2546                 }
2547             }
2548
2549             return this.asyncRequest(method, uri, cb, data);
2550         },
2551
2552         serializeForm : function(form) {
2553             if(typeof form == 'string') {
2554                 form = (document.getElementById(form) || document.forms[form]);
2555             }
2556
2557             var el, name, val, disabled, data = '', hasSubmit = false;
2558             for (var i = 0; i < form.elements.length; i++) {
2559                 el = form.elements[i];
2560                 disabled = form.elements[i].disabled;
2561                 name = form.elements[i].name;
2562                 val = form.elements[i].value;
2563
2564                 if (!disabled && name){
2565                     switch (el.type)
2566                             {
2567                         case 'select-one':
2568                         case 'select-multiple':
2569                             for (var j = 0; j < el.options.length; j++) {
2570                                 if (el.options[j].selected) {
2571                                     if (Roo.isIE) {
2572                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2573                                     }
2574                                     else {
2575                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2576                                     }
2577                                 }
2578                             }
2579                             break;
2580                         case 'radio':
2581                         case 'checkbox':
2582                             if (el.checked) {
2583                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2584                             }
2585                             break;
2586                         case 'file':
2587
2588                         case undefined:
2589
2590                         case 'reset':
2591
2592                         case 'button':
2593
2594                             break;
2595                         case 'submit':
2596                             if(hasSubmit == false) {
2597                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2598                                 hasSubmit = true;
2599                             }
2600                             break;
2601                         default:
2602                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2603                             break;
2604                     }
2605                 }
2606             }
2607             data = data.substr(0, data.length - 1);
2608             return data;
2609         },
2610
2611         headers:{},
2612
2613         hasHeaders:false,
2614
2615         useDefaultHeader:true,
2616
2617         defaultPostHeader:'application/x-www-form-urlencoded',
2618
2619         useDefaultXhrHeader:true,
2620
2621         defaultXhrHeader:'XMLHttpRequest',
2622
2623         hasDefaultHeaders:true,
2624
2625         defaultHeaders:{},
2626
2627         poll:{},
2628
2629         timeout:{},
2630
2631         pollInterval:50,
2632
2633         transactionId:0,
2634
2635         setProgId:function(id)
2636         {
2637             this.activeX.unshift(id);
2638         },
2639
2640         setDefaultPostHeader:function(b)
2641         {
2642             this.useDefaultHeader = b;
2643         },
2644
2645         setDefaultXhrHeader:function(b)
2646         {
2647             this.useDefaultXhrHeader = b;
2648         },
2649
2650         setPollingInterval:function(i)
2651         {
2652             if (typeof i == 'number' && isFinite(i)) {
2653                 this.pollInterval = i;
2654             }
2655         },
2656
2657         createXhrObject:function(transactionId)
2658         {
2659             var obj,http;
2660             try
2661             {
2662
2663                 http = new XMLHttpRequest();
2664
2665                 obj = { conn:http, tId:transactionId };
2666             }
2667             catch(e)
2668             {
2669                 for (var i = 0; i < this.activeX.length; ++i) {
2670                     try
2671                     {
2672
2673                         http = new ActiveXObject(this.activeX[i]);
2674
2675                         obj = { conn:http, tId:transactionId };
2676                         break;
2677                     }
2678                     catch(e) {
2679                     }
2680                 }
2681             }
2682             finally
2683             {
2684                 return obj;
2685             }
2686         },
2687
2688         getConnectionObject:function()
2689         {
2690             var o;
2691             var tId = this.transactionId;
2692
2693             try
2694             {
2695                 o = this.createXhrObject(tId);
2696                 if (o) {
2697                     this.transactionId++;
2698                 }
2699             }
2700             catch(e) {
2701             }
2702             finally
2703             {
2704                 return o;
2705             }
2706         },
2707
2708         asyncRequest:function(method, uri, callback, postData)
2709         {
2710             var o = this.getConnectionObject();
2711
2712             if (!o) {
2713                 return null;
2714             }
2715             else {
2716                 o.conn.open(method, uri, true);
2717
2718                 if (this.useDefaultXhrHeader) {
2719                     if (!this.defaultHeaders['X-Requested-With']) {
2720                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2721                     }
2722                 }
2723
2724                 if(postData && this.useDefaultHeader){
2725                     this.initHeader('Content-Type', this.defaultPostHeader);
2726                 }
2727
2728                  if (this.hasDefaultHeaders || this.hasHeaders) {
2729                     this.setHeader(o);
2730                 }
2731
2732                 this.handleReadyState(o, callback);
2733                 o.conn.send(postData || null);
2734
2735                 return o;
2736             }
2737         },
2738
2739         handleReadyState:function(o, callback)
2740         {
2741             var oConn = this;
2742
2743             if (callback && callback.timeout) {
2744                 
2745                 this.timeout[o.tId] = window.setTimeout(function() {
2746                     oConn.abort(o, callback, true);
2747                 }, callback.timeout);
2748             }
2749
2750             this.poll[o.tId] = window.setInterval(
2751                     function() {
2752                         if (o.conn && o.conn.readyState == 4) {
2753                             window.clearInterval(oConn.poll[o.tId]);
2754                             delete oConn.poll[o.tId];
2755
2756                             if(callback && callback.timeout) {
2757                                 window.clearTimeout(oConn.timeout[o.tId]);
2758                                 delete oConn.timeout[o.tId];
2759                             }
2760
2761                             oConn.handleTransactionResponse(o, callback);
2762                         }
2763                     }
2764                     , this.pollInterval);
2765         },
2766
2767         handleTransactionResponse:function(o, callback, isAbort)
2768         {
2769
2770             if (!callback) {
2771                 this.releaseObject(o);
2772                 return;
2773             }
2774
2775             var httpStatus, responseObject;
2776
2777             try
2778             {
2779                 if (o.conn.status !== undefined && o.conn.status != 0) {
2780                     httpStatus = o.conn.status;
2781                 }
2782                 else {
2783                     httpStatus = 13030;
2784                 }
2785             }
2786             catch(e) {
2787
2788
2789                 httpStatus = 13030;
2790             }
2791
2792             if (httpStatus >= 200 && httpStatus < 300) {
2793                 responseObject = this.createResponseObject(o, callback.argument);
2794                 if (callback.success) {
2795                     if (!callback.scope) {
2796                         callback.success(responseObject);
2797                     }
2798                     else {
2799
2800
2801                         callback.success.apply(callback.scope, [responseObject]);
2802                     }
2803                 }
2804             }
2805             else {
2806                 switch (httpStatus) {
2807
2808                     case 12002:
2809                     case 12029:
2810                     case 12030:
2811                     case 12031:
2812                     case 12152:
2813                     case 13030:
2814                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
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                         break;
2824                     default:
2825                         responseObject = this.createResponseObject(o, callback.argument);
2826                         if (callback.failure) {
2827                             if (!callback.scope) {
2828                                 callback.failure(responseObject);
2829                             }
2830                             else {
2831                                 callback.failure.apply(callback.scope, [responseObject]);
2832                             }
2833                         }
2834                 }
2835             }
2836
2837             this.releaseObject(o);
2838             responseObject = null;
2839         },
2840
2841         createResponseObject:function(o, callbackArg)
2842         {
2843             var obj = {};
2844             var headerObj = {};
2845
2846             try
2847             {
2848                 var headerStr = o.conn.getAllResponseHeaders();
2849                 var header = headerStr.split('\n');
2850                 for (var i = 0; i < header.length; i++) {
2851                     var delimitPos = header[i].indexOf(':');
2852                     if (delimitPos != -1) {
2853                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2854                     }
2855                 }
2856             }
2857             catch(e) {
2858             }
2859
2860             obj.tId = o.tId;
2861             obj.status = o.conn.status;
2862             obj.statusText = o.conn.statusText;
2863             obj.getResponseHeader = headerObj;
2864             obj.getAllResponseHeaders = headerStr;
2865             obj.responseText = o.conn.responseText;
2866             obj.responseXML = o.conn.responseXML;
2867
2868             if (typeof callbackArg !== undefined) {
2869                 obj.argument = callbackArg;
2870             }
2871
2872             return obj;
2873         },
2874
2875         createExceptionObject:function(tId, callbackArg, isAbort)
2876         {
2877             var COMM_CODE = 0;
2878             var COMM_ERROR = 'communication failure';
2879             var ABORT_CODE = -1;
2880             var ABORT_ERROR = 'transaction aborted';
2881
2882             var obj = {};
2883
2884             obj.tId = tId;
2885             if (isAbort) {
2886                 obj.status = ABORT_CODE;
2887                 obj.statusText = ABORT_ERROR;
2888             }
2889             else {
2890                 obj.status = COMM_CODE;
2891                 obj.statusText = COMM_ERROR;
2892             }
2893
2894             if (callbackArg) {
2895                 obj.argument = callbackArg;
2896             }
2897
2898             return obj;
2899         },
2900
2901         initHeader:function(label, value, isDefault)
2902         {
2903             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2904
2905             if (headerObj[label] === undefined) {
2906                 headerObj[label] = value;
2907             }
2908             else {
2909
2910
2911                 headerObj[label] = value + "," + headerObj[label];
2912             }
2913
2914             if (isDefault) {
2915                 this.hasDefaultHeaders = true;
2916             }
2917             else {
2918                 this.hasHeaders = true;
2919             }
2920         },
2921
2922
2923         setHeader:function(o)
2924         {
2925             if (this.hasDefaultHeaders) {
2926                 for (var prop in this.defaultHeaders) {
2927                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2928                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2929                     }
2930                 }
2931             }
2932
2933             if (this.hasHeaders) {
2934                 for (var prop in this.headers) {
2935                     if (this.headers.hasOwnProperty(prop)) {
2936                         o.conn.setRequestHeader(prop, this.headers[prop]);
2937                     }
2938                 }
2939                 this.headers = {};
2940                 this.hasHeaders = false;
2941             }
2942         },
2943
2944         resetDefaultHeaders:function() {
2945             delete this.defaultHeaders;
2946             this.defaultHeaders = {};
2947             this.hasDefaultHeaders = false;
2948         },
2949
2950         abort:function(o, callback, isTimeout)
2951         {
2952             if(this.isCallInProgress(o)) {
2953                 o.conn.abort();
2954                 window.clearInterval(this.poll[o.tId]);
2955                 delete this.poll[o.tId];
2956                 if (isTimeout) {
2957                     delete this.timeout[o.tId];
2958                 }
2959
2960                 this.handleTransactionResponse(o, callback, true);
2961
2962                 return true;
2963             }
2964             else {
2965                 return false;
2966             }
2967         },
2968
2969
2970         isCallInProgress:function(o)
2971         {
2972             if (o && o.conn) {
2973                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2974             }
2975             else {
2976
2977                 return false;
2978             }
2979         },
2980
2981
2982         releaseObject:function(o)
2983         {
2984
2985             o.conn = null;
2986
2987             o = null;
2988         },
2989
2990         activeX:[
2991         'MSXML2.XMLHTTP.3.0',
2992         'MSXML2.XMLHTTP',
2993         'Microsoft.XMLHTTP'
2994         ]
2995
2996
2997     };
2998 })();/*
2999  * Portions of this file are based on pieces of Yahoo User Interface Library
3000  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3001  * YUI licensed under the BSD License:
3002  * http://developer.yahoo.net/yui/license.txt
3003  * <script type="text/javascript">
3004  *
3005  */
3006
3007 Roo.lib.Region = function(t, r, b, l) {
3008     this.top = t;
3009     this[1] = t;
3010     this.right = r;
3011     this.bottom = b;
3012     this.left = l;
3013     this[0] = l;
3014 };
3015
3016
3017 Roo.lib.Region.prototype = {
3018     contains : function(region) {
3019         return ( region.left >= this.left &&
3020                  region.right <= this.right &&
3021                  region.top >= this.top &&
3022                  region.bottom <= this.bottom    );
3023
3024     },
3025
3026     getArea : function() {
3027         return ( (this.bottom - this.top) * (this.right - this.left) );
3028     },
3029
3030     intersect : function(region) {
3031         var t = Math.max(this.top, region.top);
3032         var r = Math.min(this.right, region.right);
3033         var b = Math.min(this.bottom, region.bottom);
3034         var l = Math.max(this.left, region.left);
3035
3036         if (b >= t && r >= l) {
3037             return new Roo.lib.Region(t, r, b, l);
3038         } else {
3039             return null;
3040         }
3041     },
3042     union : function(region) {
3043         var t = Math.min(this.top, region.top);
3044         var r = Math.max(this.right, region.right);
3045         var b = Math.max(this.bottom, region.bottom);
3046         var l = Math.min(this.left, region.left);
3047
3048         return new Roo.lib.Region(t, r, b, l);
3049     },
3050
3051     adjust : function(t, l, b, r) {
3052         this.top += t;
3053         this.left += l;
3054         this.right += r;
3055         this.bottom += b;
3056         return this;
3057     }
3058 };
3059
3060 Roo.lib.Region.getRegion = function(el) {
3061     var p = Roo.lib.Dom.getXY(el);
3062
3063     var t = p[1];
3064     var r = p[0] + el.offsetWidth;
3065     var b = p[1] + el.offsetHeight;
3066     var l = p[0];
3067
3068     return new Roo.lib.Region(t, r, b, l);
3069 };
3070 /*
3071  * Portions of this file are based on pieces of Yahoo User Interface Library
3072  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3073  * YUI licensed under the BSD License:
3074  * http://developer.yahoo.net/yui/license.txt
3075  * <script type="text/javascript">
3076  *
3077  */
3078 //@@dep Roo.lib.Region
3079
3080
3081 Roo.lib.Point = function(x, y) {
3082     if (x instanceof Array) {
3083         y = x[1];
3084         x = x[0];
3085     }
3086     this.x = this.right = this.left = this[0] = x;
3087     this.y = this.top = this.bottom = this[1] = y;
3088 };
3089
3090 Roo.lib.Point.prototype = new Roo.lib.Region();
3091 /*
3092  * Portions of this file are based on pieces of Yahoo User Interface Library
3093  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3094  * YUI licensed under the BSD License:
3095  * http://developer.yahoo.net/yui/license.txt
3096  * <script type="text/javascript">
3097  *
3098  */
3099  
3100 (function() {   
3101
3102     Roo.lib.Anim = {
3103         scroll : function(el, args, duration, easing, cb, scope) {
3104             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3105         },
3106
3107         motion : function(el, args, duration, easing, cb, scope) {
3108             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3109         },
3110
3111         color : function(el, args, duration, easing, cb, scope) {
3112             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3113         },
3114
3115         run : function(el, args, duration, easing, cb, scope, type) {
3116             type = type || Roo.lib.AnimBase;
3117             if (typeof easing == "string") {
3118                 easing = Roo.lib.Easing[easing];
3119             }
3120             var anim = new type(el, args, duration, easing);
3121             anim.animateX(function() {
3122                 Roo.callback(cb, scope);
3123             });
3124             return anim;
3125         }
3126     };
3127 })();/*
3128  * Portions of this file are based on pieces of Yahoo User Interface Library
3129  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3130  * YUI licensed under the BSD License:
3131  * http://developer.yahoo.net/yui/license.txt
3132  * <script type="text/javascript">
3133  *
3134  */
3135
3136 (function() {    
3137     var libFlyweight;
3138     
3139     function fly(el) {
3140         if (!libFlyweight) {
3141             libFlyweight = new Roo.Element.Flyweight();
3142         }
3143         libFlyweight.dom = el;
3144         return libFlyweight;
3145     }
3146
3147     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3148     
3149    
3150     
3151     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3152         if (el) {
3153             this.init(el, attributes, duration, method);
3154         }
3155     };
3156
3157     Roo.lib.AnimBase.fly = fly;
3158     
3159     
3160     
3161     Roo.lib.AnimBase.prototype = {
3162
3163         toString: function() {
3164             var el = this.getEl();
3165             var id = el.id || el.tagName;
3166             return ("Anim " + id);
3167         },
3168
3169         patterns: {
3170             noNegatives:        /width|height|opacity|padding/i,
3171             offsetAttribute:  /^((width|height)|(top|left))$/,
3172             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3173             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3174         },
3175
3176
3177         doMethod: function(attr, start, end) {
3178             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3179         },
3180
3181
3182         setAttribute: function(attr, val, unit) {
3183             if (this.patterns.noNegatives.test(attr)) {
3184                 val = (val > 0) ? val : 0;
3185             }
3186
3187             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3188         },
3189
3190
3191         getAttribute: function(attr) {
3192             var el = this.getEl();
3193             var val = fly(el).getStyle(attr);
3194
3195             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3196                 return parseFloat(val);
3197             }
3198
3199             var a = this.patterns.offsetAttribute.exec(attr) || [];
3200             var pos = !!( a[3] );
3201             var box = !!( a[2] );
3202
3203
3204             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3205                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3206             } else {
3207                 val = 0;
3208             }
3209
3210             return val;
3211         },
3212
3213
3214         getDefaultUnit: function(attr) {
3215             if (this.patterns.defaultUnit.test(attr)) {
3216                 return 'px';
3217             }
3218
3219             return '';
3220         },
3221
3222         animateX : function(callback, scope) {
3223             var f = function() {
3224                 this.onComplete.removeListener(f);
3225                 if (typeof callback == "function") {
3226                     callback.call(scope || this, this);
3227                 }
3228             };
3229             this.onComplete.addListener(f, this);
3230             this.animate();
3231         },
3232
3233
3234         setRuntimeAttribute: function(attr) {
3235             var start;
3236             var end;
3237             var attributes = this.attributes;
3238
3239             this.runtimeAttributes[attr] = {};
3240
3241             var isset = function(prop) {
3242                 return (typeof prop !== 'undefined');
3243             };
3244
3245             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3246                 return false;
3247             }
3248
3249             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3250
3251
3252             if (isset(attributes[attr]['to'])) {
3253                 end = attributes[attr]['to'];
3254             } else if (isset(attributes[attr]['by'])) {
3255                 if (start.constructor == Array) {
3256                     end = [];
3257                     for (var i = 0, len = start.length; i < len; ++i) {
3258                         end[i] = start[i] + attributes[attr]['by'][i];
3259                     }
3260                 } else {
3261                     end = start + attributes[attr]['by'];
3262                 }
3263             }
3264
3265             this.runtimeAttributes[attr].start = start;
3266             this.runtimeAttributes[attr].end = end;
3267
3268
3269             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3270         },
3271
3272
3273         init: function(el, attributes, duration, method) {
3274
3275             var isAnimated = false;
3276
3277
3278             var startTime = null;
3279
3280
3281             var actualFrames = 0;
3282
3283
3284             el = Roo.getDom(el);
3285
3286
3287             this.attributes = attributes || {};
3288
3289
3290             this.duration = duration || 1;
3291
3292
3293             this.method = method || Roo.lib.Easing.easeNone;
3294
3295
3296             this.useSeconds = true;
3297
3298
3299             this.currentFrame = 0;
3300
3301
3302             this.totalFrames = Roo.lib.AnimMgr.fps;
3303
3304
3305             this.getEl = function() {
3306                 return el;
3307             };
3308
3309
3310             this.isAnimated = function() {
3311                 return isAnimated;
3312             };
3313
3314
3315             this.getStartTime = function() {
3316                 return startTime;
3317             };
3318
3319             this.runtimeAttributes = {};
3320
3321
3322             this.animate = function() {
3323                 if (this.isAnimated()) {
3324                     return false;
3325                 }
3326
3327                 this.currentFrame = 0;
3328
3329                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3330
3331                 Roo.lib.AnimMgr.registerElement(this);
3332             };
3333
3334
3335             this.stop = function(finish) {
3336                 if (finish) {
3337                     this.currentFrame = this.totalFrames;
3338                     this._onTween.fire();
3339                 }
3340                 Roo.lib.AnimMgr.stop(this);
3341             };
3342
3343             var onStart = function() {
3344                 this.onStart.fire();
3345
3346                 this.runtimeAttributes = {};
3347                 for (var attr in this.attributes) {
3348                     this.setRuntimeAttribute(attr);
3349                 }
3350
3351                 isAnimated = true;
3352                 actualFrames = 0;
3353                 startTime = new Date();
3354             };
3355
3356
3357             var onTween = function() {
3358                 var data = {
3359                     duration: new Date() - this.getStartTime(),
3360                     currentFrame: this.currentFrame
3361                 };
3362
3363                 data.toString = function() {
3364                     return (
3365                             'duration: ' + data.duration +
3366                             ', currentFrame: ' + data.currentFrame
3367                             );
3368                 };
3369
3370                 this.onTween.fire(data);
3371
3372                 var runtimeAttributes = this.runtimeAttributes;
3373
3374                 for (var attr in runtimeAttributes) {
3375                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3376                 }
3377
3378                 actualFrames += 1;
3379             };
3380
3381             var onComplete = function() {
3382                 var actual_duration = (new Date() - startTime) / 1000 ;
3383
3384                 var data = {
3385                     duration: actual_duration,
3386                     frames: actualFrames,
3387                     fps: actualFrames / actual_duration
3388                 };
3389
3390                 data.toString = function() {
3391                     return (
3392                             'duration: ' + data.duration +
3393                             ', frames: ' + data.frames +
3394                             ', fps: ' + data.fps
3395                             );
3396                 };
3397
3398                 isAnimated = false;
3399                 actualFrames = 0;
3400                 this.onComplete.fire(data);
3401             };
3402
3403
3404             this._onStart = new Roo.util.Event(this);
3405             this.onStart = new Roo.util.Event(this);
3406             this.onTween = new Roo.util.Event(this);
3407             this._onTween = new Roo.util.Event(this);
3408             this.onComplete = new Roo.util.Event(this);
3409             this._onComplete = new Roo.util.Event(this);
3410             this._onStart.addListener(onStart);
3411             this._onTween.addListener(onTween);
3412             this._onComplete.addListener(onComplete);
3413         }
3414     };
3415 })();
3416 /*
3417  * Portions of this file are based on pieces of Yahoo User Interface Library
3418  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3419  * YUI licensed under the BSD License:
3420  * http://developer.yahoo.net/yui/license.txt
3421  * <script type="text/javascript">
3422  *
3423  */
3424
3425 Roo.lib.AnimMgr = new function() {
3426
3427     var thread = null;
3428
3429
3430     var queue = [];
3431
3432
3433     var tweenCount = 0;
3434
3435
3436     this.fps = 1000;
3437
3438
3439     this.delay = 1;
3440
3441
3442     this.registerElement = function(tween) {
3443         queue[queue.length] = tween;
3444         tweenCount += 1;
3445         tween._onStart.fire();
3446         this.start();
3447     };
3448
3449
3450     this.unRegister = function(tween, index) {
3451         tween._onComplete.fire();
3452         index = index || getIndex(tween);
3453         if (index != -1) {
3454             queue.splice(index, 1);
3455         }
3456
3457         tweenCount -= 1;
3458         if (tweenCount <= 0) {
3459             this.stop();
3460         }
3461     };
3462
3463
3464     this.start = function() {
3465         if (thread === null) {
3466             thread = setInterval(this.run, this.delay);
3467         }
3468     };
3469
3470
3471     this.stop = function(tween) {
3472         if (!tween) {
3473             clearInterval(thread);
3474
3475             for (var i = 0, len = queue.length; i < len; ++i) {
3476                 if (queue[0].isAnimated()) {
3477                     this.unRegister(queue[0], 0);
3478                 }
3479             }
3480
3481             queue = [];
3482             thread = null;
3483             tweenCount = 0;
3484         }
3485         else {
3486             this.unRegister(tween);
3487         }
3488     };
3489
3490
3491     this.run = function() {
3492         for (var i = 0, len = queue.length; i < len; ++i) {
3493             var tween = queue[i];
3494             if (!tween || !tween.isAnimated()) {
3495                 continue;
3496             }
3497
3498             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3499             {
3500                 tween.currentFrame += 1;
3501
3502                 if (tween.useSeconds) {
3503                     correctFrame(tween);
3504                 }
3505                 tween._onTween.fire();
3506             }
3507             else {
3508                 Roo.lib.AnimMgr.stop(tween, i);
3509             }
3510         }
3511     };
3512
3513     var getIndex = function(anim) {
3514         for (var i = 0, len = queue.length; i < len; ++i) {
3515             if (queue[i] == anim) {
3516                 return i;
3517             }
3518         }
3519         return -1;
3520     };
3521
3522
3523     var correctFrame = function(tween) {
3524         var frames = tween.totalFrames;
3525         var frame = tween.currentFrame;
3526         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3527         var elapsed = (new Date() - tween.getStartTime());
3528         var tweak = 0;
3529
3530         if (elapsed < tween.duration * 1000) {
3531             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3532         } else {
3533             tweak = frames - (frame + 1);
3534         }
3535         if (tweak > 0 && isFinite(tweak)) {
3536             if (tween.currentFrame + tweak >= frames) {
3537                 tweak = frames - (frame + 1);
3538             }
3539
3540             tween.currentFrame += tweak;
3541         }
3542     };
3543 };
3544
3545     /*
3546  * Portions of this file are based on pieces of Yahoo User Interface Library
3547  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3548  * YUI licensed under the BSD License:
3549  * http://developer.yahoo.net/yui/license.txt
3550  * <script type="text/javascript">
3551  *
3552  */
3553 Roo.lib.Bezier = new function() {
3554
3555         this.getPosition = function(points, t) {
3556             var n = points.length;
3557             var tmp = [];
3558
3559             for (var i = 0; i < n; ++i) {
3560                 tmp[i] = [points[i][0], points[i][1]];
3561             }
3562
3563             for (var j = 1; j < n; ++j) {
3564                 for (i = 0; i < n - j; ++i) {
3565                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3566                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3567                 }
3568             }
3569
3570             return [ tmp[0][0], tmp[0][1] ];
3571
3572         };
3573     };/*
3574  * Portions of this file are based on pieces of Yahoo User Interface Library
3575  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3576  * YUI licensed under the BSD License:
3577  * http://developer.yahoo.net/yui/license.txt
3578  * <script type="text/javascript">
3579  *
3580  */
3581 (function() {
3582
3583     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3584         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3585     };
3586
3587     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3588
3589     var fly = Roo.lib.AnimBase.fly;
3590     var Y = Roo.lib;
3591     var superclass = Y.ColorAnim.superclass;
3592     var proto = Y.ColorAnim.prototype;
3593
3594     proto.toString = function() {
3595         var el = this.getEl();
3596         var id = el.id || el.tagName;
3597         return ("ColorAnim " + id);
3598     };
3599
3600     proto.patterns.color = /color$/i;
3601     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3602     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3603     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3604     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3605
3606
3607     proto.parseColor = function(s) {
3608         if (s.length == 3) {
3609             return s;
3610         }
3611
3612         var c = this.patterns.hex.exec(s);
3613         if (c && c.length == 4) {
3614             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3615         }
3616
3617         c = this.patterns.rgb.exec(s);
3618         if (c && c.length == 4) {
3619             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3620         }
3621
3622         c = this.patterns.hex3.exec(s);
3623         if (c && c.length == 4) {
3624             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3625         }
3626
3627         return null;
3628     };
3629     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653     proto.getAttribute = function(attr) {
3654         var el = this.getEl();
3655         if (this.patterns.color.test(attr)) {
3656             var val = fly(el).getStyle(attr);
3657
3658             if (this.patterns.transparent.test(val)) {
3659                 var parent = el.parentNode;
3660                 val = fly(parent).getStyle(attr);
3661
3662                 while (parent && this.patterns.transparent.test(val)) {
3663                     parent = parent.parentNode;
3664                     val = fly(parent).getStyle(attr);
3665                     if (parent.tagName.toUpperCase() == 'HTML') {
3666                         val = '#fff';
3667                     }
3668                 }
3669             }
3670         } else {
3671             val = superclass.getAttribute.call(this, attr);
3672         }
3673
3674         return val;
3675     };
3676
3677     proto.doMethod = function(attr, start, end) {
3678         var val;
3679
3680         if (this.patterns.color.test(attr)) {
3681             val = [];
3682             for (var i = 0, len = start.length; i < len; ++i) {
3683                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3684             }
3685
3686             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3687         }
3688         else {
3689             val = superclass.doMethod.call(this, attr, start, end);
3690         }
3691
3692         return val;
3693     };
3694
3695     proto.setRuntimeAttribute = function(attr) {
3696         superclass.setRuntimeAttribute.call(this, attr);
3697
3698         if (this.patterns.color.test(attr)) {
3699             var attributes = this.attributes;
3700             var start = this.parseColor(this.runtimeAttributes[attr].start);
3701             var end = this.parseColor(this.runtimeAttributes[attr].end);
3702
3703             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3704                 end = this.parseColor(attributes[attr].by);
3705
3706                 for (var i = 0, len = start.length; i < len; ++i) {
3707                     end[i] = start[i] + end[i];
3708                 }
3709             }
3710
3711             this.runtimeAttributes[attr].start = start;
3712             this.runtimeAttributes[attr].end = end;
3713         }
3714     };
3715 })();
3716
3717 /*
3718  * Portions of this file are based on pieces of Yahoo User Interface Library
3719  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3720  * YUI licensed under the BSD License:
3721  * http://developer.yahoo.net/yui/license.txt
3722  * <script type="text/javascript">
3723  *
3724  */
3725 Roo.lib.Easing = {
3726
3727
3728     easeNone: function (t, b, c, d) {
3729         return c * t / d + b;
3730     },
3731
3732
3733     easeIn: function (t, b, c, d) {
3734         return c * (t /= d) * t + b;
3735     },
3736
3737
3738     easeOut: function (t, b, c, d) {
3739         return -c * (t /= d) * (t - 2) + b;
3740     },
3741
3742
3743     easeBoth: function (t, b, c, d) {
3744         if ((t /= d / 2) < 1) {
3745             return c / 2 * t * t + b;
3746         }
3747
3748         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3749     },
3750
3751
3752     easeInStrong: function (t, b, c, d) {
3753         return c * (t /= d) * t * t * t + b;
3754     },
3755
3756
3757     easeOutStrong: function (t, b, c, d) {
3758         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3759     },
3760
3761
3762     easeBothStrong: function (t, b, c, d) {
3763         if ((t /= d / 2) < 1) {
3764             return c / 2 * t * t * t * t + b;
3765         }
3766
3767         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3768     },
3769
3770
3771
3772     elasticIn: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3792     },
3793
3794
3795     elasticOut: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799         if ((t /= d) == 1) {
3800             return b + c;
3801         }
3802         if (!p) {
3803             p = d * .3;
3804         }
3805
3806         if (!a || a < Math.abs(c)) {
3807             a = c;
3808             var s = p / 4;
3809         }
3810         else {
3811             var s = p / (2 * Math.PI) * Math.asin(c / a);
3812         }
3813
3814         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3815     },
3816
3817
3818     elasticBoth: function (t, b, c, d, a, p) {
3819         if (t == 0) {
3820             return b;
3821         }
3822
3823         if ((t /= d / 2) == 2) {
3824             return b + c;
3825         }
3826
3827         if (!p) {
3828             p = d * (.3 * 1.5);
3829         }
3830
3831         if (!a || a < Math.abs(c)) {
3832             a = c;
3833             var s = p / 4;
3834         }
3835         else {
3836             var s = p / (2 * Math.PI) * Math.asin(c / a);
3837         }
3838
3839         if (t < 1) {
3840             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3841                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3842         }
3843         return a * Math.pow(2, -10 * (t -= 1)) *
3844                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3845     },
3846
3847
3848
3849     backIn: function (t, b, c, d, s) {
3850         if (typeof s == 'undefined') {
3851             s = 1.70158;
3852         }
3853         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3854     },
3855
3856
3857     backOut: function (t, b, c, d, s) {
3858         if (typeof s == 'undefined') {
3859             s = 1.70158;
3860         }
3861         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3862     },
3863
3864
3865     backBoth: function (t, b, c, d, s) {
3866         if (typeof s == 'undefined') {
3867             s = 1.70158;
3868         }
3869
3870         if ((t /= d / 2 ) < 1) {
3871             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3872         }
3873         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3874     },
3875
3876
3877     bounceIn: function (t, b, c, d) {
3878         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3879     },
3880
3881
3882     bounceOut: function (t, b, c, d) {
3883         if ((t /= d) < (1 / 2.75)) {
3884             return c * (7.5625 * t * t) + b;
3885         } else if (t < (2 / 2.75)) {
3886             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3887         } else if (t < (2.5 / 2.75)) {
3888             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3889         }
3890         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3891     },
3892
3893
3894     bounceBoth: function (t, b, c, d) {
3895         if (t < d / 2) {
3896             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3897         }
3898         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3899     }
3900 };/*
3901  * Portions of this file are based on pieces of Yahoo User Interface Library
3902  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3903  * YUI licensed under the BSD License:
3904  * http://developer.yahoo.net/yui/license.txt
3905  * <script type="text/javascript">
3906  *
3907  */
3908     (function() {
3909         Roo.lib.Motion = function(el, attributes, duration, method) {
3910             if (el) {
3911                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3912             }
3913         };
3914
3915         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3916
3917
3918         var Y = Roo.lib;
3919         var superclass = Y.Motion.superclass;
3920         var proto = Y.Motion.prototype;
3921
3922         proto.toString = function() {
3923             var el = this.getEl();
3924             var id = el.id || el.tagName;
3925             return ("Motion " + id);
3926         };
3927
3928         proto.patterns.points = /^points$/i;
3929
3930         proto.setAttribute = function(attr, val, unit) {
3931             if (this.patterns.points.test(attr)) {
3932                 unit = unit || 'px';
3933                 superclass.setAttribute.call(this, 'left', val[0], unit);
3934                 superclass.setAttribute.call(this, 'top', val[1], unit);
3935             } else {
3936                 superclass.setAttribute.call(this, attr, val, unit);
3937             }
3938         };
3939
3940         proto.getAttribute = function(attr) {
3941             if (this.patterns.points.test(attr)) {
3942                 var val = [
3943                         superclass.getAttribute.call(this, 'left'),
3944                         superclass.getAttribute.call(this, 'top')
3945                         ];
3946             } else {
3947                 val = superclass.getAttribute.call(this, attr);
3948             }
3949
3950             return val;
3951         };
3952
3953         proto.doMethod = function(attr, start, end) {
3954             var val = null;
3955
3956             if (this.patterns.points.test(attr)) {
3957                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3958                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3959             } else {
3960                 val = superclass.doMethod.call(this, attr, start, end);
3961             }
3962             return val;
3963         };
3964
3965         proto.setRuntimeAttribute = function(attr) {
3966             if (this.patterns.points.test(attr)) {
3967                 var el = this.getEl();
3968                 var attributes = this.attributes;
3969                 var start;
3970                 var control = attributes['points']['control'] || [];
3971                 var end;
3972                 var i, len;
3973
3974                 if (control.length > 0 && !(control[0] instanceof Array)) {
3975                     control = [control];
3976                 } else {
3977                     var tmp = [];
3978                     for (i = 0,len = control.length; i < len; ++i) {
3979                         tmp[i] = control[i];
3980                     }
3981                     control = tmp;
3982                 }
3983
3984                 Roo.fly(el).position();
3985
3986                 if (isset(attributes['points']['from'])) {
3987                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3988                 }
3989                 else {
3990                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3991                 }
3992
3993                 start = this.getAttribute('points');
3994
3995
3996                 if (isset(attributes['points']['to'])) {
3997                     end = translateValues.call(this, attributes['points']['to'], start);
3998
3999                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4000                     for (i = 0,len = control.length; i < len; ++i) {
4001                         control[i] = translateValues.call(this, control[i], start);
4002                     }
4003
4004
4005                 } else if (isset(attributes['points']['by'])) {
4006                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4007
4008                     for (i = 0,len = control.length; i < len; ++i) {
4009                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4010                     }
4011                 }
4012
4013                 this.runtimeAttributes[attr] = [start];
4014
4015                 if (control.length > 0) {
4016                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4017                 }
4018
4019                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4020             }
4021             else {
4022                 superclass.setRuntimeAttribute.call(this, attr);
4023             }
4024         };
4025
4026         var translateValues = function(val, start) {
4027             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4028             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4029
4030             return val;
4031         };
4032
4033         var isset = function(prop) {
4034             return (typeof prop !== 'undefined');
4035         };
4036     })();
4037 /*
4038  * Portions of this file are based on pieces of Yahoo User Interface Library
4039  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4040  * YUI licensed under the BSD License:
4041  * http://developer.yahoo.net/yui/license.txt
4042  * <script type="text/javascript">
4043  *
4044  */
4045     (function() {
4046         Roo.lib.Scroll = function(el, attributes, duration, method) {
4047             if (el) {
4048                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4049             }
4050         };
4051
4052         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4053
4054
4055         var Y = Roo.lib;
4056         var superclass = Y.Scroll.superclass;
4057         var proto = Y.Scroll.prototype;
4058
4059         proto.toString = function() {
4060             var el = this.getEl();
4061             var id = el.id || el.tagName;
4062             return ("Scroll " + id);
4063         };
4064
4065         proto.doMethod = function(attr, start, end) {
4066             var val = null;
4067
4068             if (attr == 'scroll') {
4069                 val = [
4070                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4071                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4072                         ];
4073
4074             } else {
4075                 val = superclass.doMethod.call(this, attr, start, end);
4076             }
4077             return val;
4078         };
4079
4080         proto.getAttribute = function(attr) {
4081             var val = null;
4082             var el = this.getEl();
4083
4084             if (attr == 'scroll') {
4085                 val = [ el.scrollLeft, el.scrollTop ];
4086             } else {
4087                 val = superclass.getAttribute.call(this, attr);
4088             }
4089
4090             return val;
4091         };
4092
4093         proto.setAttribute = function(attr, val, unit) {
4094             var el = this.getEl();
4095
4096             if (attr == 'scroll') {
4097                 el.scrollLeft = val[0];
4098                 el.scrollTop = val[1];
4099             } else {
4100                 superclass.setAttribute.call(this, attr, val, unit);
4101             }
4102         };
4103     })();
4104 /*
4105  * Based on:
4106  * Ext JS Library 1.1.1
4107  * Copyright(c) 2006-2007, Ext JS, LLC.
4108  *
4109  * Originally Released Under LGPL - original licence link has changed is not relivant.
4110  *
4111  * Fork - LGPL
4112  * <script type="text/javascript">
4113  */
4114
4115
4116 // nasty IE9 hack - what a pile of crap that is..
4117
4118  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4119     Range.prototype.createContextualFragment = function (html) {
4120         var doc = window.document;
4121         var container = doc.createElement("div");
4122         container.innerHTML = html;
4123         var frag = doc.createDocumentFragment(), n;
4124         while ((n = container.firstChild)) {
4125             frag.appendChild(n);
4126         }
4127         return frag;
4128     };
4129 }
4130
4131 /**
4132  * @class Roo.DomHelper
4133  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4134  * 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>.
4135  * @singleton
4136  */
4137 Roo.DomHelper = function(){
4138     var tempTableEl = null;
4139     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4140     var tableRe = /^table|tbody|tr|td$/i;
4141     var xmlns = {};
4142     // build as innerHTML where available
4143     /** @ignore */
4144     var createHtml = function(o){
4145         if(typeof o == 'string'){
4146             return o;
4147         }
4148         var b = "";
4149         if(!o.tag){
4150             o.tag = "div";
4151         }
4152         b += "<" + o.tag;
4153         for(var attr in o){
4154             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4155             if(attr == "style"){
4156                 var s = o["style"];
4157                 if(typeof s == "function"){
4158                     s = s.call();
4159                 }
4160                 if(typeof s == "string"){
4161                     b += ' style="' + s + '"';
4162                 }else if(typeof s == "object"){
4163                     b += ' style="';
4164                     for(var key in s){
4165                         if(typeof s[key] != "function"){
4166                             b += key + ":" + s[key] + ";";
4167                         }
4168                     }
4169                     b += '"';
4170                 }
4171             }else{
4172                 if(attr == "cls"){
4173                     b += ' class="' + o["cls"] + '"';
4174                 }else if(attr == "htmlFor"){
4175                     b += ' for="' + o["htmlFor"] + '"';
4176                 }else{
4177                     b += " " + attr + '="' + o[attr] + '"';
4178                 }
4179             }
4180         }
4181         if(emptyTags.test(o.tag)){
4182             b += "/>";
4183         }else{
4184             b += ">";
4185             var cn = o.children || o.cn;
4186             if(cn){
4187                 //http://bugs.kde.org/show_bug.cgi?id=71506
4188                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4189                     for(var i = 0, len = cn.length; i < len; i++) {
4190                         b += createHtml(cn[i], b);
4191                     }
4192                 }else{
4193                     b += createHtml(cn, b);
4194                 }
4195             }
4196             if(o.html){
4197                 b += o.html;
4198             }
4199             b += "</" + o.tag + ">";
4200         }
4201         return b;
4202     };
4203
4204     // build as dom
4205     /** @ignore */
4206     var createDom = function(o, parentNode){
4207          
4208         // defininition craeted..
4209         var ns = false;
4210         if (o.ns && o.ns != 'html') {
4211                
4212             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4213                 xmlns[o.ns] = o.xmlns;
4214                 ns = o.xmlns;
4215             }
4216             if (typeof(xmlns[o.ns]) == 'undefined') {
4217                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4218             }
4219             ns = xmlns[o.ns];
4220         }
4221         
4222         
4223         if (typeof(o) == 'string') {
4224             return parentNode.appendChild(document.createTextNode(o));
4225         }
4226         o.tag = o.tag || div;
4227         if (o.ns && Roo.isIE) {
4228             ns = false;
4229             o.tag = o.ns + ':' + o.tag;
4230             
4231         }
4232         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4233         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4234         for(var attr in o){
4235             
4236             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4237                     attr == "style" || typeof o[attr] == "function") continue;
4238                     
4239             if(attr=="cls" && Roo.isIE){
4240                 el.className = o["cls"];
4241             }else{
4242                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4243                 else { 
4244                     el[attr] = o[attr];
4245                 }
4246             }
4247         }
4248         Roo.DomHelper.applyStyles(el, o.style);
4249         var cn = o.children || o.cn;
4250         if(cn){
4251             //http://bugs.kde.org/show_bug.cgi?id=71506
4252              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4253                 for(var i = 0, len = cn.length; i < len; i++) {
4254                     createDom(cn[i], el);
4255                 }
4256             }else{
4257                 createDom(cn, el);
4258             }
4259         }
4260         if(o.html){
4261             el.innerHTML = o.html;
4262         }
4263         if(parentNode){
4264            parentNode.appendChild(el);
4265         }
4266         return el;
4267     };
4268
4269     var ieTable = function(depth, s, h, e){
4270         tempTableEl.innerHTML = [s, h, e].join('');
4271         var i = -1, el = tempTableEl;
4272         while(++i < depth){
4273             el = el.firstChild;
4274         }
4275         return el;
4276     };
4277
4278     // kill repeat to save bytes
4279     var ts = '<table>',
4280         te = '</table>',
4281         tbs = ts+'<tbody>',
4282         tbe = '</tbody>'+te,
4283         trs = tbs + '<tr>',
4284         tre = '</tr>'+tbe;
4285
4286     /**
4287      * @ignore
4288      * Nasty code for IE's broken table implementation
4289      */
4290     var insertIntoTable = function(tag, where, el, html){
4291         if(!tempTableEl){
4292             tempTableEl = document.createElement('div');
4293         }
4294         var node;
4295         var before = null;
4296         if(tag == 'td'){
4297             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4298                 return;
4299             }
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303             } else{
4304                 before = el.nextSibling;
4305                 el = el.parentNode;
4306             }
4307             node = ieTable(4, trs, html, tre);
4308         }
4309         else if(tag == 'tr'){
4310             if(where == 'beforebegin'){
4311                 before = el;
4312                 el = el.parentNode;
4313                 node = ieTable(3, tbs, html, tbe);
4314             } else if(where == 'afterend'){
4315                 before = el.nextSibling;
4316                 el = el.parentNode;
4317                 node = ieTable(3, tbs, html, tbe);
4318             } else{ // INTO a TR
4319                 if(where == 'afterbegin'){
4320                     before = el.firstChild;
4321                 }
4322                 node = ieTable(4, trs, html, tre);
4323             }
4324         } else if(tag == 'tbody'){
4325             if(where == 'beforebegin'){
4326                 before = el;
4327                 el = el.parentNode;
4328                 node = ieTable(2, ts, html, te);
4329             } else if(where == 'afterend'){
4330                 before = el.nextSibling;
4331                 el = el.parentNode;
4332                 node = ieTable(2, ts, html, te);
4333             } else{
4334                 if(where == 'afterbegin'){
4335                     before = el.firstChild;
4336                 }
4337                 node = ieTable(3, tbs, html, tbe);
4338             }
4339         } else{ // TABLE
4340             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4341                 return;
4342             }
4343             if(where == 'afterbegin'){
4344                 before = el.firstChild;
4345             }
4346             node = ieTable(2, ts, html, te);
4347         }
4348         el.insertBefore(node, before);
4349         return node;
4350     };
4351
4352     return {
4353     /** True to force the use of DOM instead of html fragments @type Boolean */
4354     useDom : false,
4355
4356     /**
4357      * Returns the markup for the passed Element(s) config
4358      * @param {Object} o The Dom object spec (and children)
4359      * @return {String}
4360      */
4361     markup : function(o){
4362         return createHtml(o);
4363     },
4364
4365     /**
4366      * Applies a style specification to an element
4367      * @param {String/HTMLElement} el The element to apply styles to
4368      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4369      * a function which returns such a specification.
4370      */
4371     applyStyles : function(el, styles){
4372         if(styles){
4373            el = Roo.fly(el);
4374            if(typeof styles == "string"){
4375                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4376                var matches;
4377                while ((matches = re.exec(styles)) != null){
4378                    el.setStyle(matches[1], matches[2]);
4379                }
4380            }else if (typeof styles == "object"){
4381                for (var style in styles){
4382                   el.setStyle(style, styles[style]);
4383                }
4384            }else if (typeof styles == "function"){
4385                 Roo.DomHelper.applyStyles(el, styles.call());
4386            }
4387         }
4388     },
4389
4390     /**
4391      * Inserts an HTML fragment into the Dom
4392      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4393      * @param {HTMLElement} el The context element
4394      * @param {String} html The HTML fragmenet
4395      * @return {HTMLElement} The new node
4396      */
4397     insertHtml : function(where, el, html){
4398         where = where.toLowerCase();
4399         if(el.insertAdjacentHTML){
4400             if(tableRe.test(el.tagName)){
4401                 var rs;
4402                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4403                     return rs;
4404                 }
4405             }
4406             switch(where){
4407                 case "beforebegin":
4408                     el.insertAdjacentHTML('BeforeBegin', html);
4409                     return el.previousSibling;
4410                 case "afterbegin":
4411                     el.insertAdjacentHTML('AfterBegin', html);
4412                     return el.firstChild;
4413                 case "beforeend":
4414                     el.insertAdjacentHTML('BeforeEnd', html);
4415                     return el.lastChild;
4416                 case "afterend":
4417                     el.insertAdjacentHTML('AfterEnd', html);
4418                     return el.nextSibling;
4419             }
4420             throw 'Illegal insertion point -> "' + where + '"';
4421         }
4422         var range = el.ownerDocument.createRange();
4423         var frag;
4424         switch(where){
4425              case "beforebegin":
4426                 range.setStartBefore(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el);
4429                 return el.previousSibling;
4430              case "afterbegin":
4431                 if(el.firstChild){
4432                     range.setStartBefore(el.firstChild);
4433                     frag = range.createContextualFragment(html);
4434                     el.insertBefore(frag, el.firstChild);
4435                     return el.firstChild;
4436                 }else{
4437                     el.innerHTML = html;
4438                     return el.firstChild;
4439                 }
4440             case "beforeend":
4441                 if(el.lastChild){
4442                     range.setStartAfter(el.lastChild);
4443                     frag = range.createContextualFragment(html);
4444                     el.appendChild(frag);
4445                     return el.lastChild;
4446                 }else{
4447                     el.innerHTML = html;
4448                     return el.lastChild;
4449                 }
4450             case "afterend":
4451                 range.setStartAfter(el);
4452                 frag = range.createContextualFragment(html);
4453                 el.parentNode.insertBefore(frag, el.nextSibling);
4454                 return el.nextSibling;
4455             }
4456             throw 'Illegal insertion point -> "' + where + '"';
4457     },
4458
4459     /**
4460      * Creates new Dom element(s) and inserts them before el
4461      * @param {String/HTMLElement/Element} el The context element
4462      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4463      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4464      * @return {HTMLElement/Roo.Element} The new node
4465      */
4466     insertBefore : function(el, o, returnElement){
4467         return this.doInsert(el, o, returnElement, "beforeBegin");
4468     },
4469
4470     /**
4471      * Creates new Dom element(s) and inserts them after el
4472      * @param {String/HTMLElement/Element} el The context element
4473      * @param {Object} o The Dom object spec (and children)
4474      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4475      * @return {HTMLElement/Roo.Element} The new node
4476      */
4477     insertAfter : function(el, o, returnElement){
4478         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and inserts them as the first child of el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     insertFirst : function(el, o, returnElement){
4489         return this.doInsert(el, o, returnElement, "afterBegin");
4490     },
4491
4492     // private
4493     doInsert : function(el, o, returnElement, pos, sibling){
4494         el = Roo.getDom(el);
4495         var newNode;
4496         if(this.useDom || o.ns){
4497             newNode = createDom(o, null);
4498             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4499         }else{
4500             var html = createHtml(o);
4501             newNode = this.insertHtml(pos, el, html);
4502         }
4503         return returnElement ? Roo.get(newNode, true) : newNode;
4504     },
4505
4506     /**
4507      * Creates new Dom element(s) and appends them to el
4508      * @param {String/HTMLElement/Element} el The context element
4509      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4510      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4511      * @return {HTMLElement/Roo.Element} The new node
4512      */
4513     append : function(el, o, returnElement){
4514         el = Roo.getDom(el);
4515         var newNode;
4516         if(this.useDom || o.ns){
4517             newNode = createDom(o, null);
4518             el.appendChild(newNode);
4519         }else{
4520             var html = createHtml(o);
4521             newNode = this.insertHtml("beforeEnd", el, html);
4522         }
4523         return returnElement ? Roo.get(newNode, true) : newNode;
4524     },
4525
4526     /**
4527      * Creates new Dom element(s) and overwrites the contents of el with them
4528      * @param {String/HTMLElement/Element} el The context element
4529      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4530      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4531      * @return {HTMLElement/Roo.Element} The new node
4532      */
4533     overwrite : function(el, o, returnElement){
4534         el = Roo.getDom(el);
4535         if (o.ns) {
4536           
4537             while (el.childNodes.length) {
4538                 el.removeChild(el.firstChild);
4539             }
4540             createDom(o, el);
4541         } else {
4542             el.innerHTML = createHtml(o);   
4543         }
4544         
4545         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4546     },
4547
4548     /**
4549      * Creates a new Roo.DomHelper.Template from the Dom object spec
4550      * @param {Object} o The Dom object spec (and children)
4551      * @return {Roo.DomHelper.Template} The new template
4552      */
4553     createTemplate : function(o){
4554         var html = createHtml(o);
4555         return new Roo.Template(html);
4556     }
4557     };
4558 }();
4559 /*
4560  * Based on:
4561  * Ext JS Library 1.1.1
4562  * Copyright(c) 2006-2007, Ext JS, LLC.
4563  *
4564  * Originally Released Under LGPL - original licence link has changed is not relivant.
4565  *
4566  * Fork - LGPL
4567  * <script type="text/javascript">
4568  */
4569  
4570 /**
4571 * @class Roo.Template
4572 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4573 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4574 * Usage:
4575 <pre><code>
4576 var t = new Roo.Template({
4577     html :  '&lt;div name="{id}"&gt;' + 
4578         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4579         '&lt;/div&gt;',
4580     myformat: function (value, allValues) {
4581         return 'XX' + value;
4582     }
4583 });
4584 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4585 </code></pre>
4586 * For more information see this blog post with examples:
4587 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4588      - Create Elements using DOM, HTML fragments and Templates</a>. 
4589 * @constructor
4590 * @param {Object} cfg - Configuration object.
4591 */
4592 Roo.Template = function(cfg){
4593     // BC!
4594     if(cfg instanceof Array){
4595         cfg = cfg.join("");
4596     }else if(arguments.length > 1){
4597         cfg = Array.prototype.join.call(arguments, "");
4598     }
4599     
4600     
4601     if (typeof(cfg) == 'object') {
4602         Roo.apply(this,cfg)
4603     } else {
4604         // bc
4605         this.html = cfg;
4606     }
4607     if (this.url) {
4608         this.load();
4609     }
4610     
4611 };
4612 Roo.Template.prototype = {
4613     
4614     /**
4615      * @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..
4616      *                    it should be fixed so that template is observable...
4617      */
4618     url : false,
4619     /**
4620      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4621      */
4622     html : '',
4623     /**
4624      * Returns an HTML fragment of this template with the specified values applied.
4625      * @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'})
4626      * @return {String} The HTML fragment
4627      */
4628     applyTemplate : function(values){
4629         try {
4630            
4631             if(this.compiled){
4632                 return this.compiled(values);
4633             }
4634             var useF = this.disableFormats !== true;
4635             var fm = Roo.util.Format, tpl = this;
4636             var fn = function(m, name, format, args){
4637                 if(format && useF){
4638                     if(format.substr(0, 5) == "this."){
4639                         return tpl.call(format.substr(5), values[name], values);
4640                     }else{
4641                         if(args){
4642                             // quoted values are required for strings in compiled templates, 
4643                             // but for non compiled we need to strip them
4644                             // quoted reversed for jsmin
4645                             var re = /^\s*['"](.*)["']\s*$/;
4646                             args = args.split(',');
4647                             for(var i = 0, len = args.length; i < len; i++){
4648                                 args[i] = args[i].replace(re, "$1");
4649                             }
4650                             args = [values[name]].concat(args);
4651                         }else{
4652                             args = [values[name]];
4653                         }
4654                         return fm[format].apply(fm, args);
4655                     }
4656                 }else{
4657                     return values[name] !== undefined ? values[name] : "";
4658                 }
4659             };
4660             return this.html.replace(this.re, fn);
4661         } catch (e) {
4662             Roo.log(e);
4663             throw e;
4664         }
4665          
4666     },
4667     
4668     loading : false,
4669       
4670     load : function ()
4671     {
4672          
4673         if (this.loading) {
4674             return;
4675         }
4676         var _t = this;
4677         
4678         this.loading = true;
4679         this.compiled = false;
4680         
4681         var cx = new Roo.data.Connection();
4682         cx.request({
4683             url : this.url,
4684             method : 'GET',
4685             success : function (response) {
4686                 _t.loading = false;
4687                 _t.html = response.responseText;
4688                 _t.url = false;
4689                 _t.compile();
4690              },
4691             failure : function(response) {
4692                 Roo.log("Template failed to load from " + _t.url);
4693                 _t.loading = false;
4694             }
4695         });
4696     },
4697
4698     /**
4699      * Sets the HTML used as the template and optionally compiles it.
4700      * @param {String} html
4701      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4702      * @return {Roo.Template} this
4703      */
4704     set : function(html, compile){
4705         this.html = html;
4706         this.compiled = null;
4707         if(compile){
4708             this.compile();
4709         }
4710         return this;
4711     },
4712     
4713     /**
4714      * True to disable format functions (defaults to false)
4715      * @type Boolean
4716      */
4717     disableFormats : false,
4718     
4719     /**
4720     * The regular expression used to match template variables 
4721     * @type RegExp
4722     * @property 
4723     */
4724     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4725     
4726     /**
4727      * Compiles the template into an internal function, eliminating the RegEx overhead.
4728      * @return {Roo.Template} this
4729      */
4730     compile : function(){
4731         var fm = Roo.util.Format;
4732         var useF = this.disableFormats !== true;
4733         var sep = Roo.isGecko ? "+" : ",";
4734         var fn = function(m, name, format, args){
4735             if(format && useF){
4736                 args = args ? ',' + args : "";
4737                 if(format.substr(0, 5) != "this."){
4738                     format = "fm." + format + '(';
4739                 }else{
4740                     format = 'this.call("'+ format.substr(5) + '", ';
4741                     args = ", values";
4742                 }
4743             }else{
4744                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4745             }
4746             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4747         };
4748         var body;
4749         // branched to use + in gecko and [].join() in others
4750         if(Roo.isGecko){
4751             body = "this.compiled = function(values){ return '" +
4752                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4753                     "';};";
4754         }else{
4755             body = ["this.compiled = function(values){ return ['"];
4756             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4757             body.push("'].join('');};");
4758             body = body.join('');
4759         }
4760         /**
4761          * eval:var:values
4762          * eval:var:fm
4763          */
4764         eval(body);
4765         return this;
4766     },
4767     
4768     // private function used to call members
4769     call : function(fnName, value, allValues){
4770         return this[fnName](value, allValues);
4771     },
4772     
4773     /**
4774      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4775      * @param {String/HTMLElement/Roo.Element} el The context element
4776      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4777      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4778      * @return {HTMLElement/Roo.Element} The new node or Element
4779      */
4780     insertFirst: function(el, values, returnElement){
4781         return this.doInsert('afterBegin', el, values, returnElement);
4782     },
4783
4784     /**
4785      * Applies the supplied values to the template and inserts the new node(s) before el.
4786      * @param {String/HTMLElement/Roo.Element} el The context element
4787      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4788      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4789      * @return {HTMLElement/Roo.Element} The new node or Element
4790      */
4791     insertBefore: function(el, values, returnElement){
4792         return this.doInsert('beforeBegin', el, values, returnElement);
4793     },
4794
4795     /**
4796      * Applies the supplied values to the template and inserts the new node(s) after el.
4797      * @param {String/HTMLElement/Roo.Element} el The context element
4798      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4799      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4800      * @return {HTMLElement/Roo.Element} The new node or Element
4801      */
4802     insertAfter : function(el, values, returnElement){
4803         return this.doInsert('afterEnd', el, values, returnElement);
4804     },
4805     
4806     /**
4807      * Applies the supplied values to the template and appends the new node(s) to el.
4808      * @param {String/HTMLElement/Roo.Element} el The context element
4809      * @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'})
4810      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4811      * @return {HTMLElement/Roo.Element} The new node or Element
4812      */
4813     append : function(el, values, returnElement){
4814         return this.doInsert('beforeEnd', el, values, returnElement);
4815     },
4816
4817     doInsert : function(where, el, values, returnEl){
4818         el = Roo.getDom(el);
4819         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4820         return returnEl ? Roo.get(newNode, true) : newNode;
4821     },
4822
4823     /**
4824      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4825      * @param {String/HTMLElement/Roo.Element} el The context element
4826      * @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'})
4827      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4828      * @return {HTMLElement/Roo.Element} The new node or Element
4829      */
4830     overwrite : function(el, values, returnElement){
4831         el = Roo.getDom(el);
4832         el.innerHTML = this.applyTemplate(values);
4833         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4834     }
4835 };
4836 /**
4837  * Alias for {@link #applyTemplate}
4838  * @method
4839  */
4840 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4841
4842 // backwards compat
4843 Roo.DomHelper.Template = Roo.Template;
4844
4845 /**
4846  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4847  * @param {String/HTMLElement} el A DOM element or its id
4848  * @returns {Roo.Template} The created template
4849  * @static
4850  */
4851 Roo.Template.from = function(el){
4852     el = Roo.getDom(el);
4853     return new Roo.Template(el.value || el.innerHTML);
4854 };/*
4855  * Based on:
4856  * Ext JS Library 1.1.1
4857  * Copyright(c) 2006-2007, Ext JS, LLC.
4858  *
4859  * Originally Released Under LGPL - original licence link has changed is not relivant.
4860  *
4861  * Fork - LGPL
4862  * <script type="text/javascript">
4863  */
4864  
4865
4866 /*
4867  * This is code is also distributed under MIT license for use
4868  * with jQuery and prototype JavaScript libraries.
4869  */
4870 /**
4871  * @class Roo.DomQuery
4872 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).
4873 <p>
4874 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>
4875
4876 <p>
4877 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.
4878 </p>
4879 <h4>Element Selectors:</h4>
4880 <ul class="list">
4881     <li> <b>*</b> any element</li>
4882     <li> <b>E</b> an element with the tag E</li>
4883     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4884     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4885     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4886     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4887 </ul>
4888 <h4>Attribute Selectors:</h4>
4889 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4890 <ul class="list">
4891     <li> <b>E[foo]</b> has an attribute "foo"</li>
4892     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4893     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4894     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4895     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4896     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4897     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4898 </ul>
4899 <h4>Pseudo Classes:</h4>
4900 <ul class="list">
4901     <li> <b>E:first-child</b> E is the first child of its parent</li>
4902     <li> <b>E:last-child</b> E is the last child of its parent</li>
4903     <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>
4904     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4905     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4906     <li> <b>E:only-child</b> E is the only child of its parent</li>
4907     <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>
4908     <li> <b>E:first</b> the first E in the resultset</li>
4909     <li> <b>E:last</b> the last E in the resultset</li>
4910     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4911     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4912     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4913     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4914     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4915     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4916     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4917     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4918     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4919 </ul>
4920 <h4>CSS Value Selectors:</h4>
4921 <ul class="list">
4922     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4923     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4924     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4925     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4926     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4927     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4928 </ul>
4929  * @singleton
4930  */
4931 Roo.DomQuery = function(){
4932     var cache = {}, simpleCache = {}, valueCache = {};
4933     var nonSpace = /\S/;
4934     var trimRe = /^\s+|\s+$/g;
4935     var tplRe = /\{(\d+)\}/g;
4936     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4937     var tagTokenRe = /^(#)?([\w-\*]+)/;
4938     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4939
4940     function child(p, index){
4941         var i = 0;
4942         var n = p.firstChild;
4943         while(n){
4944             if(n.nodeType == 1){
4945                if(++i == index){
4946                    return n;
4947                }
4948             }
4949             n = n.nextSibling;
4950         }
4951         return null;
4952     };
4953
4954     function next(n){
4955         while((n = n.nextSibling) && n.nodeType != 1);
4956         return n;
4957     };
4958
4959     function prev(n){
4960         while((n = n.previousSibling) && n.nodeType != 1);
4961         return n;
4962     };
4963
4964     function children(d){
4965         var n = d.firstChild, ni = -1;
4966             while(n){
4967                 var nx = n.nextSibling;
4968                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4969                     d.removeChild(n);
4970                 }else{
4971                     n.nodeIndex = ++ni;
4972                 }
4973                 n = nx;
4974             }
4975             return this;
4976         };
4977
4978     function byClassName(c, a, v){
4979         if(!v){
4980             return c;
4981         }
4982         var r = [], ri = -1, cn;
4983         for(var i = 0, ci; ci = c[i]; i++){
4984             if((' '+ci.className+' ').indexOf(v) != -1){
4985                 r[++ri] = ci;
4986             }
4987         }
4988         return r;
4989     };
4990
4991     function attrValue(n, attr){
4992         if(!n.tagName && typeof n.length != "undefined"){
4993             n = n[0];
4994         }
4995         if(!n){
4996             return null;
4997         }
4998         if(attr == "for"){
4999             return n.htmlFor;
5000         }
5001         if(attr == "class" || attr == "className"){
5002             return n.className;
5003         }
5004         return n.getAttribute(attr) || n[attr];
5005
5006     };
5007
5008     function getNodes(ns, mode, tagName){
5009         var result = [], ri = -1, cs;
5010         if(!ns){
5011             return result;
5012         }
5013         tagName = tagName || "*";
5014         if(typeof ns.getElementsByTagName != "undefined"){
5015             ns = [ns];
5016         }
5017         if(!mode){
5018             for(var i = 0, ni; ni = ns[i]; i++){
5019                 cs = ni.getElementsByTagName(tagName);
5020                 for(var j = 0, ci; ci = cs[j]; j++){
5021                     result[++ri] = ci;
5022                 }
5023             }
5024         }else if(mode == "/" || mode == ">"){
5025             var utag = tagName.toUpperCase();
5026             for(var i = 0, ni, cn; ni = ns[i]; i++){
5027                 cn = ni.children || ni.childNodes;
5028                 for(var j = 0, cj; cj = cn[j]; j++){
5029                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5030                         result[++ri] = cj;
5031                     }
5032                 }
5033             }
5034         }else if(mode == "+"){
5035             var utag = tagName.toUpperCase();
5036             for(var i = 0, n; n = ns[i]; i++){
5037                 while((n = n.nextSibling) && n.nodeType != 1);
5038                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5039                     result[++ri] = n;
5040                 }
5041             }
5042         }else if(mode == "~"){
5043             for(var i = 0, n; n = ns[i]; i++){
5044                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5045                 if(n){
5046                     result[++ri] = n;
5047                 }
5048             }
5049         }
5050         return result;
5051     };
5052
5053     function concat(a, b){
5054         if(b.slice){
5055             return a.concat(b);
5056         }
5057         for(var i = 0, l = b.length; i < l; i++){
5058             a[a.length] = b[i];
5059         }
5060         return a;
5061     }
5062
5063     function byTag(cs, tagName){
5064         if(cs.tagName || cs == document){
5065             cs = [cs];
5066         }
5067         if(!tagName){
5068             return cs;
5069         }
5070         var r = [], ri = -1;
5071         tagName = tagName.toLowerCase();
5072         for(var i = 0, ci; ci = cs[i]; i++){
5073             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5074                 r[++ri] = ci;
5075             }
5076         }
5077         return r;
5078     };
5079
5080     function byId(cs, attr, id){
5081         if(cs.tagName || cs == document){
5082             cs = [cs];
5083         }
5084         if(!id){
5085             return cs;
5086         }
5087         var r = [], ri = -1;
5088         for(var i = 0,ci; ci = cs[i]; i++){
5089             if(ci && ci.id == id){
5090                 r[++ri] = ci;
5091                 return r;
5092             }
5093         }
5094         return r;
5095     };
5096
5097     function byAttribute(cs, attr, value, op, custom){
5098         var r = [], ri = -1, st = custom=="{";
5099         var f = Roo.DomQuery.operators[op];
5100         for(var i = 0, ci; ci = cs[i]; i++){
5101             var a;
5102             if(st){
5103                 a = Roo.DomQuery.getStyle(ci, attr);
5104             }
5105             else if(attr == "class" || attr == "className"){
5106                 a = ci.className;
5107             }else if(attr == "for"){
5108                 a = ci.htmlFor;
5109             }else if(attr == "href"){
5110                 a = ci.getAttribute("href", 2);
5111             }else{
5112                 a = ci.getAttribute(attr);
5113             }
5114             if((f && f(a, value)) || (!f && a)){
5115                 r[++ri] = ci;
5116             }
5117         }
5118         return r;
5119     };
5120
5121     function byPseudo(cs, name, value){
5122         return Roo.DomQuery.pseudos[name](cs, value);
5123     };
5124
5125     // This is for IE MSXML which does not support expandos.
5126     // IE runs the same speed using setAttribute, however FF slows way down
5127     // and Safari completely fails so they need to continue to use expandos.
5128     var isIE = window.ActiveXObject ? true : false;
5129
5130     // this eval is stop the compressor from
5131     // renaming the variable to something shorter
5132     
5133     /** eval:var:batch */
5134     var batch = 30803; 
5135
5136     var key = 30803;
5137
5138     function nodupIEXml(cs){
5139         var d = ++key;
5140         cs[0].setAttribute("_nodup", d);
5141         var r = [cs[0]];
5142         for(var i = 1, len = cs.length; i < len; i++){
5143             var c = cs[i];
5144             if(!c.getAttribute("_nodup") != d){
5145                 c.setAttribute("_nodup", d);
5146                 r[r.length] = c;
5147             }
5148         }
5149         for(var i = 0, len = cs.length; i < len; i++){
5150             cs[i].removeAttribute("_nodup");
5151         }
5152         return r;
5153     }
5154
5155     function nodup(cs){
5156         if(!cs){
5157             return [];
5158         }
5159         var len = cs.length, c, i, r = cs, cj, ri = -1;
5160         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5161             return cs;
5162         }
5163         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5164             return nodupIEXml(cs);
5165         }
5166         var d = ++key;
5167         cs[0]._nodup = d;
5168         for(i = 1; c = cs[i]; i++){
5169             if(c._nodup != d){
5170                 c._nodup = d;
5171             }else{
5172                 r = [];
5173                 for(var j = 0; j < i; j++){
5174                     r[++ri] = cs[j];
5175                 }
5176                 for(j = i+1; cj = cs[j]; j++){
5177                     if(cj._nodup != d){
5178                         cj._nodup = d;
5179                         r[++ri] = cj;
5180                     }
5181                 }
5182                 return r;
5183             }
5184         }
5185         return r;
5186     }
5187
5188     function quickDiffIEXml(c1, c2){
5189         var d = ++key;
5190         for(var i = 0, len = c1.length; i < len; i++){
5191             c1[i].setAttribute("_qdiff", d);
5192         }
5193         var r = [];
5194         for(var i = 0, len = c2.length; i < len; i++){
5195             if(c2[i].getAttribute("_qdiff") != d){
5196                 r[r.length] = c2[i];
5197             }
5198         }
5199         for(var i = 0, len = c1.length; i < len; i++){
5200            c1[i].removeAttribute("_qdiff");
5201         }
5202         return r;
5203     }
5204
5205     function quickDiff(c1, c2){
5206         var len1 = c1.length;
5207         if(!len1){
5208             return c2;
5209         }
5210         if(isIE && c1[0].selectSingleNode){
5211             return quickDiffIEXml(c1, c2);
5212         }
5213         var d = ++key;
5214         for(var i = 0; i < len1; i++){
5215             c1[i]._qdiff = d;
5216         }
5217         var r = [];
5218         for(var i = 0, len = c2.length; i < len; i++){
5219             if(c2[i]._qdiff != d){
5220                 r[r.length] = c2[i];
5221             }
5222         }
5223         return r;
5224     }
5225
5226     function quickId(ns, mode, root, id){
5227         if(ns == root){
5228            var d = root.ownerDocument || root;
5229            return d.getElementById(id);
5230         }
5231         ns = getNodes(ns, mode, "*");
5232         return byId(ns, null, id);
5233     }
5234
5235     return {
5236         getStyle : function(el, name){
5237             return Roo.fly(el).getStyle(name);
5238         },
5239         /**
5240          * Compiles a selector/xpath query into a reusable function. The returned function
5241          * takes one parameter "root" (optional), which is the context node from where the query should start.
5242          * @param {String} selector The selector/xpath query
5243          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5244          * @return {Function}
5245          */
5246         compile : function(path, type){
5247             type = type || "select";
5248             
5249             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5250             var q = path, mode, lq;
5251             var tk = Roo.DomQuery.matchers;
5252             var tklen = tk.length;
5253             var mm;
5254
5255             // accept leading mode switch
5256             var lmode = q.match(modeRe);
5257             if(lmode && lmode[1]){
5258                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5259                 q = q.replace(lmode[1], "");
5260             }
5261             // strip leading slashes
5262             while(path.substr(0, 1)=="/"){
5263                 path = path.substr(1);
5264             }
5265
5266             while(q && lq != q){
5267                 lq = q;
5268                 var tm = q.match(tagTokenRe);
5269                 if(type == "select"){
5270                     if(tm){
5271                         if(tm[1] == "#"){
5272                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5273                         }else{
5274                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5275                         }
5276                         q = q.replace(tm[0], "");
5277                     }else if(q.substr(0, 1) != '@'){
5278                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5279                     }
5280                 }else{
5281                     if(tm){
5282                         if(tm[1] == "#"){
5283                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5284                         }else{
5285                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5286                         }
5287                         q = q.replace(tm[0], "");
5288                     }
5289                 }
5290                 while(!(mm = q.match(modeRe))){
5291                     var matched = false;
5292                     for(var j = 0; j < tklen; j++){
5293                         var t = tk[j];
5294                         var m = q.match(t.re);
5295                         if(m){
5296                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5297                                                     return m[i];
5298                                                 });
5299                             q = q.replace(m[0], "");
5300                             matched = true;
5301                             break;
5302                         }
5303                     }
5304                     // prevent infinite loop on bad selector
5305                     if(!matched){
5306                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5307                     }
5308                 }
5309                 if(mm[1]){
5310                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5311                     q = q.replace(mm[1], "");
5312                 }
5313             }
5314             fn[fn.length] = "return nodup(n);\n}";
5315             
5316              /** 
5317               * list of variables that need from compression as they are used by eval.
5318              *  eval:var:batch 
5319              *  eval:var:nodup
5320              *  eval:var:byTag
5321              *  eval:var:ById
5322              *  eval:var:getNodes
5323              *  eval:var:quickId
5324              *  eval:var:mode
5325              *  eval:var:root
5326              *  eval:var:n
5327              *  eval:var:byClassName
5328              *  eval:var:byPseudo
5329              *  eval:var:byAttribute
5330              *  eval:var:attrValue
5331              * 
5332              **/ 
5333             eval(fn.join(""));
5334             return f;
5335         },
5336
5337         /**
5338          * Selects a group of elements.
5339          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5340          * @param {Node} root (optional) The start of the query (defaults to document).
5341          * @return {Array}
5342          */
5343         select : function(path, root, type){
5344             if(!root || root == document){
5345                 root = document;
5346             }
5347             if(typeof root == "string"){
5348                 root = document.getElementById(root);
5349             }
5350             var paths = path.split(",");
5351             var results = [];
5352             for(var i = 0, len = paths.length; i < len; i++){
5353                 var p = paths[i].replace(trimRe, "");
5354                 if(!cache[p]){
5355                     cache[p] = Roo.DomQuery.compile(p);
5356                     if(!cache[p]){
5357                         throw p + " is not a valid selector";
5358                     }
5359                 }
5360                 var result = cache[p](root);
5361                 if(result && result != document){
5362                     results = results.concat(result);
5363                 }
5364             }
5365             if(paths.length > 1){
5366                 return nodup(results);
5367             }
5368             return results;
5369         },
5370
5371         /**
5372          * Selects a single element.
5373          * @param {String} selector The selector/xpath query
5374          * @param {Node} root (optional) The start of the query (defaults to document).
5375          * @return {Element}
5376          */
5377         selectNode : function(path, root){
5378             return Roo.DomQuery.select(path, root)[0];
5379         },
5380
5381         /**
5382          * Selects the value of a node, optionally replacing null with the defaultValue.
5383          * @param {String} selector The selector/xpath query
5384          * @param {Node} root (optional) The start of the query (defaults to document).
5385          * @param {String} defaultValue
5386          */
5387         selectValue : function(path, root, defaultValue){
5388             path = path.replace(trimRe, "");
5389             if(!valueCache[path]){
5390                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5391             }
5392             var n = valueCache[path](root);
5393             n = n[0] ? n[0] : n;
5394             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5395             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5396         },
5397
5398         /**
5399          * Selects the value of a node, parsing integers and floats.
5400          * @param {String} selector The selector/xpath query
5401          * @param {Node} root (optional) The start of the query (defaults to document).
5402          * @param {Number} defaultValue
5403          * @return {Number}
5404          */
5405         selectNumber : function(path, root, defaultValue){
5406             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5407             return parseFloat(v);
5408         },
5409
5410         /**
5411          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5412          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5413          * @param {String} selector The simple selector to test
5414          * @return {Boolean}
5415          */
5416         is : function(el, ss){
5417             if(typeof el == "string"){
5418                 el = document.getElementById(el);
5419             }
5420             var isArray = (el instanceof Array);
5421             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5422             return isArray ? (result.length == el.length) : (result.length > 0);
5423         },
5424
5425         /**
5426          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5427          * @param {Array} el An array of elements to filter
5428          * @param {String} selector The simple selector to test
5429          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5430          * the selector instead of the ones that match
5431          * @return {Array}
5432          */
5433         filter : function(els, ss, nonMatches){
5434             ss = ss.replace(trimRe, "");
5435             if(!simpleCache[ss]){
5436                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5437             }
5438             var result = simpleCache[ss](els);
5439             return nonMatches ? quickDiff(result, els) : result;
5440         },
5441
5442         /**
5443          * Collection of matching regular expressions and code snippets.
5444          */
5445         matchers : [{
5446                 re: /^\.([\w-]+)/,
5447                 select: 'n = byClassName(n, null, " {1} ");'
5448             }, {
5449                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5450                 select: 'n = byPseudo(n, "{1}", "{2}");'
5451             },{
5452                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5453                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5454             }, {
5455                 re: /^#([\w-]+)/,
5456                 select: 'n = byId(n, null, "{1}");'
5457             },{
5458                 re: /^@([\w-]+)/,
5459                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5460             }
5461         ],
5462
5463         /**
5464          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5465          * 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;.
5466          */
5467         operators : {
5468             "=" : function(a, v){
5469                 return a == v;
5470             },
5471             "!=" : function(a, v){
5472                 return a != v;
5473             },
5474             "^=" : function(a, v){
5475                 return a && a.substr(0, v.length) == v;
5476             },
5477             "$=" : function(a, v){
5478                 return a && a.substr(a.length-v.length) == v;
5479             },
5480             "*=" : function(a, v){
5481                 return a && a.indexOf(v) !== -1;
5482             },
5483             "%=" : function(a, v){
5484                 return (a % v) == 0;
5485             },
5486             "|=" : function(a, v){
5487                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5488             },
5489             "~=" : function(a, v){
5490                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5491             }
5492         },
5493
5494         /**
5495          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5496          * and the argument (if any) supplied in the selector.
5497          */
5498         pseudos : {
5499             "first-child" : function(c){
5500                 var r = [], ri = -1, n;
5501                 for(var i = 0, ci; ci = n = c[i]; i++){
5502                     while((n = n.previousSibling) && n.nodeType != 1);
5503                     if(!n){
5504                         r[++ri] = ci;
5505                     }
5506                 }
5507                 return r;
5508             },
5509
5510             "last-child" : function(c){
5511                 var r = [], ri = -1, n;
5512                 for(var i = 0, ci; ci = n = c[i]; i++){
5513                     while((n = n.nextSibling) && n.nodeType != 1);
5514                     if(!n){
5515                         r[++ri] = ci;
5516                     }
5517                 }
5518                 return r;
5519             },
5520
5521             "nth-child" : function(c, a) {
5522                 var r = [], ri = -1;
5523                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5524                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5525                 for(var i = 0, n; n = c[i]; i++){
5526                     var pn = n.parentNode;
5527                     if (batch != pn._batch) {
5528                         var j = 0;
5529                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5530                             if(cn.nodeType == 1){
5531                                cn.nodeIndex = ++j;
5532                             }
5533                         }
5534                         pn._batch = batch;
5535                     }
5536                     if (f == 1) {
5537                         if (l == 0 || n.nodeIndex == l){
5538                             r[++ri] = n;
5539                         }
5540                     } else if ((n.nodeIndex + l) % f == 0){
5541                         r[++ri] = n;
5542                     }
5543                 }
5544
5545                 return r;
5546             },
5547
5548             "only-child" : function(c){
5549                 var r = [], ri = -1;;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     if(!prev(ci) && !next(ci)){
5552                         r[++ri] = ci;
5553                     }
5554                 }
5555                 return r;
5556             },
5557
5558             "empty" : function(c){
5559                 var r = [], ri = -1;
5560                 for(var i = 0, ci; ci = c[i]; i++){
5561                     var cns = ci.childNodes, j = 0, cn, empty = true;
5562                     while(cn = cns[j]){
5563                         ++j;
5564                         if(cn.nodeType == 1 || cn.nodeType == 3){
5565                             empty = false;
5566                             break;
5567                         }
5568                     }
5569                     if(empty){
5570                         r[++ri] = ci;
5571                     }
5572                 }
5573                 return r;
5574             },
5575
5576             "contains" : function(c, v){
5577                 var r = [], ri = -1;
5578                 for(var i = 0, ci; ci = c[i]; i++){
5579                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5580                         r[++ri] = ci;
5581                     }
5582                 }
5583                 return r;
5584             },
5585
5586             "nodeValue" : function(c, v){
5587                 var r = [], ri = -1;
5588                 for(var i = 0, ci; ci = c[i]; i++){
5589                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5590                         r[++ri] = ci;
5591                     }
5592                 }
5593                 return r;
5594             },
5595
5596             "checked" : function(c){
5597                 var r = [], ri = -1;
5598                 for(var i = 0, ci; ci = c[i]; i++){
5599                     if(ci.checked == true){
5600                         r[++ri] = ci;
5601                     }
5602                 }
5603                 return r;
5604             },
5605
5606             "not" : function(c, ss){
5607                 return Roo.DomQuery.filter(c, ss, true);
5608             },
5609
5610             "odd" : function(c){
5611                 return this["nth-child"](c, "odd");
5612             },
5613
5614             "even" : function(c){
5615                 return this["nth-child"](c, "even");
5616             },
5617
5618             "nth" : function(c, a){
5619                 return c[a-1] || [];
5620             },
5621
5622             "first" : function(c){
5623                 return c[0] || [];
5624             },
5625
5626             "last" : function(c){
5627                 return c[c.length-1] || [];
5628             },
5629
5630             "has" : function(c, ss){
5631                 var s = Roo.DomQuery.select;
5632                 var r = [], ri = -1;
5633                 for(var i = 0, ci; ci = c[i]; i++){
5634                     if(s(ss, ci).length > 0){
5635                         r[++ri] = ci;
5636                     }
5637                 }
5638                 return r;
5639             },
5640
5641             "next" : function(c, ss){
5642                 var is = Roo.DomQuery.is;
5643                 var r = [], ri = -1;
5644                 for(var i = 0, ci; ci = c[i]; i++){
5645                     var n = next(ci);
5646                     if(n && is(n, ss)){
5647                         r[++ri] = ci;
5648                     }
5649                 }
5650                 return r;
5651             },
5652
5653             "prev" : function(c, ss){
5654                 var is = Roo.DomQuery.is;
5655                 var r = [], ri = -1;
5656                 for(var i = 0, ci; ci = c[i]; i++){
5657                     var n = prev(ci);
5658                     if(n && is(n, ss)){
5659                         r[++ri] = ci;
5660                     }
5661                 }
5662                 return r;
5663             }
5664         }
5665     };
5666 }();
5667
5668 /**
5669  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5670  * @param {String} path The selector/xpath query
5671  * @param {Node} root (optional) The start of the query (defaults to document).
5672  * @return {Array}
5673  * @member Roo
5674  * @method query
5675  */
5676 Roo.query = Roo.DomQuery.select;
5677 /*
5678  * Based on:
5679  * Ext JS Library 1.1.1
5680  * Copyright(c) 2006-2007, Ext JS, LLC.
5681  *
5682  * Originally Released Under LGPL - original licence link has changed is not relivant.
5683  *
5684  * Fork - LGPL
5685  * <script type="text/javascript">
5686  */
5687
5688 /**
5689  * @class Roo.util.Observable
5690  * Base class that provides a common interface for publishing events. Subclasses are expected to
5691  * to have a property "events" with all the events defined.<br>
5692  * For example:
5693  * <pre><code>
5694  Employee = function(name){
5695     this.name = name;
5696     this.addEvents({
5697         "fired" : true,
5698         "quit" : true
5699     });
5700  }
5701  Roo.extend(Employee, Roo.util.Observable);
5702 </code></pre>
5703  * @param {Object} config properties to use (incuding events / listeners)
5704  */
5705
5706 Roo.util.Observable = function(cfg){
5707     
5708     cfg = cfg|| {};
5709     this.addEvents(cfg.events || {});
5710     if (cfg.events) {
5711         delete cfg.events; // make sure
5712     }
5713      
5714     Roo.apply(this, cfg);
5715     
5716     if(this.listeners){
5717         this.on(this.listeners);
5718         delete this.listeners;
5719     }
5720 };
5721 Roo.util.Observable.prototype = {
5722     /** 
5723  * @cfg {Object} listeners  list of events and functions to call for this object, 
5724  * For example :
5725  * <pre><code>
5726     listeners :  { 
5727        'click' : function(e) {
5728            ..... 
5729         } ,
5730         .... 
5731     } 
5732   </code></pre>
5733  */
5734     
5735     
5736     /**
5737      * Fires the specified event with the passed parameters (minus the event name).
5738      * @param {String} eventName
5739      * @param {Object...} args Variable number of parameters are passed to handlers
5740      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5741      */
5742     fireEvent : function(){
5743         var ce = this.events[arguments[0].toLowerCase()];
5744         if(typeof ce == "object"){
5745             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5746         }else{
5747             return true;
5748         }
5749     },
5750
5751     // private
5752     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5753
5754     /**
5755      * Appends an event handler to this component
5756      * @param {String}   eventName The type of event to listen for
5757      * @param {Function} handler The method the event invokes
5758      * @param {Object}   scope (optional) The scope in which to execute the handler
5759      * function. The handler function's "this" context.
5760      * @param {Object}   options (optional) An object containing handler configuration
5761      * properties. This may contain any of the following properties:<ul>
5762      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5763      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5764      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5765      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5766      * by the specified number of milliseconds. If the event fires again within that time, the original
5767      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5768      * </ul><br>
5769      * <p>
5770      * <b>Combining Options</b><br>
5771      * Using the options argument, it is possible to combine different types of listeners:<br>
5772      * <br>
5773      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5774                 <pre><code>
5775                 el.on('click', this.onClick, this, {
5776                         single: true,
5777                 delay: 100,
5778                 forumId: 4
5779                 });
5780                 </code></pre>
5781      * <p>
5782      * <b>Attaching multiple handlers in 1 call</b><br>
5783      * The method also allows for a single argument to be passed which is a config object containing properties
5784      * which specify multiple handlers.
5785      * <pre><code>
5786                 el.on({
5787                         'click': {
5788                         fn: this.onClick,
5789                         scope: this,
5790                         delay: 100
5791                 }, 
5792                 'mouseover': {
5793                         fn: this.onMouseOver,
5794                         scope: this
5795                 },
5796                 'mouseout': {
5797                         fn: this.onMouseOut,
5798                         scope: this
5799                 }
5800                 });
5801                 </code></pre>
5802      * <p>
5803      * Or a shorthand syntax which passes the same scope object to all handlers:
5804         <pre><code>
5805                 el.on({
5806                         'click': this.onClick,
5807                 'mouseover': this.onMouseOver,
5808                 'mouseout': this.onMouseOut,
5809                 scope: this
5810                 });
5811                 </code></pre>
5812      */
5813     addListener : function(eventName, fn, scope, o){
5814         if(typeof eventName == "object"){
5815             o = eventName;
5816             for(var e in o){
5817                 if(this.filterOptRe.test(e)){
5818                     continue;
5819                 }
5820                 if(typeof o[e] == "function"){
5821                     // shared options
5822                     this.addListener(e, o[e], o.scope,  o);
5823                 }else{
5824                     // individual options
5825                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5826                 }
5827             }
5828             return;
5829         }
5830         o = (!o || typeof o == "boolean") ? {} : o;
5831         eventName = eventName.toLowerCase();
5832         var ce = this.events[eventName] || true;
5833         if(typeof ce == "boolean"){
5834             ce = new Roo.util.Event(this, eventName);
5835             this.events[eventName] = ce;
5836         }
5837         ce.addListener(fn, scope, o);
5838     },
5839
5840     /**
5841      * Removes a listener
5842      * @param {String}   eventName     The type of event to listen for
5843      * @param {Function} handler        The handler to remove
5844      * @param {Object}   scope  (optional) The scope (this object) for the handler
5845      */
5846     removeListener : function(eventName, fn, scope){
5847         var ce = this.events[eventName.toLowerCase()];
5848         if(typeof ce == "object"){
5849             ce.removeListener(fn, scope);
5850         }
5851     },
5852
5853     /**
5854      * Removes all listeners for this object
5855      */
5856     purgeListeners : function(){
5857         for(var evt in this.events){
5858             if(typeof this.events[evt] == "object"){
5859                  this.events[evt].clearListeners();
5860             }
5861         }
5862     },
5863
5864     relayEvents : function(o, events){
5865         var createHandler = function(ename){
5866             return function(){
5867                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5868             };
5869         };
5870         for(var i = 0, len = events.length; i < len; i++){
5871             var ename = events[i];
5872             if(!this.events[ename]){ this.events[ename] = true; };
5873             o.on(ename, createHandler(ename), this);
5874         }
5875     },
5876
5877     /**
5878      * Used to define events on this Observable
5879      * @param {Object} object The object with the events defined
5880      */
5881     addEvents : function(o){
5882         if(!this.events){
5883             this.events = {};
5884         }
5885         Roo.applyIf(this.events, o);
5886     },
5887
5888     /**
5889      * Checks to see if this object has any listeners for a specified event
5890      * @param {String} eventName The name of the event to check for
5891      * @return {Boolean} True if the event is being listened for, else false
5892      */
5893     hasListener : function(eventName){
5894         var e = this.events[eventName];
5895         return typeof e == "object" && e.listeners.length > 0;
5896     }
5897 };
5898 /**
5899  * Appends an event handler to this element (shorthand for addListener)
5900  * @param {String}   eventName     The type of event to listen for
5901  * @param {Function} handler        The method the event invokes
5902  * @param {Object}   scope (optional) The scope in which to execute the handler
5903  * function. The handler function's "this" context.
5904  * @param {Object}   options  (optional)
5905  * @method
5906  */
5907 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5908 /**
5909  * Removes a listener (shorthand for removeListener)
5910  * @param {String}   eventName     The type of event to listen for
5911  * @param {Function} handler        The handler to remove
5912  * @param {Object}   scope  (optional) The scope (this object) for the handler
5913  * @method
5914  */
5915 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5916
5917 /**
5918  * Starts capture on the specified Observable. All events will be passed
5919  * to the supplied function with the event name + standard signature of the event
5920  * <b>before</b> the event is fired. If the supplied function returns false,
5921  * the event will not fire.
5922  * @param {Observable} o The Observable to capture
5923  * @param {Function} fn The function to call
5924  * @param {Object} scope (optional) The scope (this object) for the fn
5925  * @static
5926  */
5927 Roo.util.Observable.capture = function(o, fn, scope){
5928     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5929 };
5930
5931 /**
5932  * Removes <b>all</b> added captures from the Observable.
5933  * @param {Observable} o The Observable to release
5934  * @static
5935  */
5936 Roo.util.Observable.releaseCapture = function(o){
5937     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5938 };
5939
5940 (function(){
5941
5942     var createBuffered = function(h, o, scope){
5943         var task = new Roo.util.DelayedTask();
5944         return function(){
5945             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5946         };
5947     };
5948
5949     var createSingle = function(h, e, fn, scope){
5950         return function(){
5951             e.removeListener(fn, scope);
5952             return h.apply(scope, arguments);
5953         };
5954     };
5955
5956     var createDelayed = function(h, o, scope){
5957         return function(){
5958             var args = Array.prototype.slice.call(arguments, 0);
5959             setTimeout(function(){
5960                 h.apply(scope, args);
5961             }, o.delay || 10);
5962         };
5963     };
5964
5965     Roo.util.Event = function(obj, name){
5966         this.name = name;
5967         this.obj = obj;
5968         this.listeners = [];
5969     };
5970
5971     Roo.util.Event.prototype = {
5972         addListener : function(fn, scope, options){
5973             var o = options || {};
5974             scope = scope || this.obj;
5975             if(!this.isListening(fn, scope)){
5976                 var l = {fn: fn, scope: scope, options: o};
5977                 var h = fn;
5978                 if(o.delay){
5979                     h = createDelayed(h, o, scope);
5980                 }
5981                 if(o.single){
5982                     h = createSingle(h, this, fn, scope);
5983                 }
5984                 if(o.buffer){
5985                     h = createBuffered(h, o, scope);
5986                 }
5987                 l.fireFn = h;
5988                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5989                     this.listeners.push(l);
5990                 }else{
5991                     this.listeners = this.listeners.slice(0);
5992                     this.listeners.push(l);
5993                 }
5994             }
5995         },
5996
5997         findListener : function(fn, scope){
5998             scope = scope || this.obj;
5999             var ls = this.listeners;
6000             for(var i = 0, len = ls.length; i < len; i++){
6001                 var l = ls[i];
6002                 if(l.fn == fn && l.scope == scope){
6003                     return i;
6004                 }
6005             }
6006             return -1;
6007         },
6008
6009         isListening : function(fn, scope){
6010             return this.findListener(fn, scope) != -1;
6011         },
6012
6013         removeListener : function(fn, scope){
6014             var index;
6015             if((index = this.findListener(fn, scope)) != -1){
6016                 if(!this.firing){
6017                     this.listeners.splice(index, 1);
6018                 }else{
6019                     this.listeners = this.listeners.slice(0);
6020                     this.listeners.splice(index, 1);
6021                 }
6022                 return true;
6023             }
6024             return false;
6025         },
6026
6027         clearListeners : function(){
6028             this.listeners = [];
6029         },
6030
6031         fire : function(){
6032             var ls = this.listeners, scope, len = ls.length;
6033             if(len > 0){
6034                 this.firing = true;
6035                 var args = Array.prototype.slice.call(arguments, 0);
6036                 for(var i = 0; i < len; i++){
6037                     var l = ls[i];
6038                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6039                         this.firing = false;
6040                         return false;
6041                     }
6042                 }
6043                 this.firing = false;
6044             }
6045             return true;
6046         }
6047     };
6048 })();/*
6049  * Based on:
6050  * Ext JS Library 1.1.1
6051  * Copyright(c) 2006-2007, Ext JS, LLC.
6052  *
6053  * Originally Released Under LGPL - original licence link has changed is not relivant.
6054  *
6055  * Fork - LGPL
6056  * <script type="text/javascript">
6057  */
6058
6059 /**
6060  * @class Roo.EventManager
6061  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6062  * several useful events directly.
6063  * See {@link Roo.EventObject} for more details on normalized event objects.
6064  * @singleton
6065  */
6066 Roo.EventManager = function(){
6067     var docReadyEvent, docReadyProcId, docReadyState = false;
6068     var resizeEvent, resizeTask, textEvent, textSize;
6069     var E = Roo.lib.Event;
6070     var D = Roo.lib.Dom;
6071
6072     
6073     
6074
6075     var fireDocReady = function(){
6076         if(!docReadyState){
6077             docReadyState = true;
6078             Roo.isReady = true;
6079             if(docReadyProcId){
6080                 clearInterval(docReadyProcId);
6081             }
6082             if(Roo.isGecko || Roo.isOpera) {
6083                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6084             }
6085             if(Roo.isIE){
6086                 var defer = document.getElementById("ie-deferred-loader");
6087                 if(defer){
6088                     defer.onreadystatechange = null;
6089                     defer.parentNode.removeChild(defer);
6090                 }
6091             }
6092             if(docReadyEvent){
6093                 docReadyEvent.fire();
6094                 docReadyEvent.clearListeners();
6095             }
6096         }
6097     };
6098     
6099     var initDocReady = function(){
6100         docReadyEvent = new Roo.util.Event();
6101         if(Roo.isGecko || Roo.isOpera) {
6102             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6103         }else if(Roo.isIE){
6104             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6105             var defer = document.getElementById("ie-deferred-loader");
6106             defer.onreadystatechange = function(){
6107                 if(this.readyState == "complete"){
6108                     fireDocReady();
6109                 }
6110             };
6111         }else if(Roo.isSafari){ 
6112             docReadyProcId = setInterval(function(){
6113                 var rs = document.readyState;
6114                 if(rs == "complete") {
6115                     fireDocReady();     
6116                  }
6117             }, 10);
6118         }
6119         // no matter what, make sure it fires on load
6120         E.on(window, "load", fireDocReady);
6121     };
6122
6123     var createBuffered = function(h, o){
6124         var task = new Roo.util.DelayedTask(h);
6125         return function(e){
6126             // create new event object impl so new events don't wipe out properties
6127             e = new Roo.EventObjectImpl(e);
6128             task.delay(o.buffer, h, null, [e]);
6129         };
6130     };
6131
6132     var createSingle = function(h, el, ename, fn){
6133         return function(e){
6134             Roo.EventManager.removeListener(el, ename, fn);
6135             h(e);
6136         };
6137     };
6138
6139     var createDelayed = function(h, o){
6140         return function(e){
6141             // create new event object impl so new events don't wipe out properties
6142             e = new Roo.EventObjectImpl(e);
6143             setTimeout(function(){
6144                 h(e);
6145             }, o.delay || 10);
6146         };
6147     };
6148     var transitionEndVal = false;
6149     
6150     var transitionEnd = function()
6151     {
6152         if (transitionEndVal) {
6153             return transitionEndVal;
6154         }
6155         var el = document.createElement('div');
6156
6157         var transEndEventNames = {
6158             WebkitTransition : 'webkitTransitionEnd',
6159             MozTransition    : 'transitionend',
6160             OTransition      : 'oTransitionEnd otransitionend',
6161             transition       : 'transitionend'
6162         };
6163     
6164         for (var name in transEndEventNames) {
6165             if (el.style[name] !== undefined) {
6166                 transitionEndVal = transEndEventNames[name];
6167                 return  transitionEndVal ;
6168             }
6169         }
6170     }
6171     
6172
6173     var listen = function(element, ename, opt, fn, scope){
6174         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6175         fn = fn || o.fn; scope = scope || o.scope;
6176         var el = Roo.getDom(element);
6177         
6178         
6179         if(!el){
6180             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6181         }
6182         
6183         if (ename == 'transitionend') {
6184             ename = transitionEnd();
6185         }
6186         var h = function(e){
6187             e = Roo.EventObject.setEvent(e);
6188             var t;
6189             if(o.delegate){
6190                 t = e.getTarget(o.delegate, el);
6191                 if(!t){
6192                     return;
6193                 }
6194             }else{
6195                 t = e.target;
6196             }
6197             if(o.stopEvent === true){
6198                 e.stopEvent();
6199             }
6200             if(o.preventDefault === true){
6201                e.preventDefault();
6202             }
6203             if(o.stopPropagation === true){
6204                 e.stopPropagation();
6205             }
6206
6207             if(o.normalized === false){
6208                 e = e.browserEvent;
6209             }
6210
6211             fn.call(scope || el, e, t, o);
6212         };
6213         if(o.delay){
6214             h = createDelayed(h, o);
6215         }
6216         if(o.single){
6217             h = createSingle(h, el, ename, fn);
6218         }
6219         if(o.buffer){
6220             h = createBuffered(h, o);
6221         }
6222         fn._handlers = fn._handlers || [];
6223         
6224         
6225         fn._handlers.push([Roo.id(el), ename, h]);
6226         
6227         
6228          
6229         E.on(el, ename, h);
6230         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6231             el.addEventListener("DOMMouseScroll", h, false);
6232             E.on(window, 'unload', function(){
6233                 el.removeEventListener("DOMMouseScroll", h, false);
6234             });
6235         }
6236         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6237             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6238         }
6239         return h;
6240     };
6241
6242     var stopListening = function(el, ename, fn){
6243         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6244         if(hds){
6245             for(var i = 0, len = hds.length; i < len; i++){
6246                 var h = hds[i];
6247                 if(h[0] == id && h[1] == ename){
6248                     hd = h[2];
6249                     hds.splice(i, 1);
6250                     break;
6251                 }
6252             }
6253         }
6254         E.un(el, ename, hd);
6255         el = Roo.getDom(el);
6256         if(ename == "mousewheel" && el.addEventListener){
6257             el.removeEventListener("DOMMouseScroll", hd, false);
6258         }
6259         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6260             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6261         }
6262     };
6263
6264     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6265     
6266     var pub = {
6267         
6268         
6269         /** 
6270          * Fix for doc tools
6271          * @scope Roo.EventManager
6272          */
6273         
6274         
6275         /** 
6276          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6277          * object with a Roo.EventObject
6278          * @param {Function} fn        The method the event invokes
6279          * @param {Object}   scope    An object that becomes the scope of the handler
6280          * @param {boolean}  override If true, the obj passed in becomes
6281          *                             the execution scope of the listener
6282          * @return {Function} The wrapped function
6283          * @deprecated
6284          */
6285         wrap : function(fn, scope, override){
6286             return function(e){
6287                 Roo.EventObject.setEvent(e);
6288                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6289             };
6290         },
6291         
6292         /**
6293      * Appends an event handler to an element (shorthand for addListener)
6294      * @param {String/HTMLElement}   element        The html element or id to assign the
6295      * @param {String}   eventName The type of event to listen for
6296      * @param {Function} handler The method the event invokes
6297      * @param {Object}   scope (optional) The scope in which to execute the handler
6298      * function. The handler function's "this" context.
6299      * @param {Object}   options (optional) An object containing handler configuration
6300      * properties. This may contain any of the following properties:<ul>
6301      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6302      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6303      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6304      * <li>preventDefault {Boolean} True to prevent the default action</li>
6305      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6306      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6307      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6308      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6309      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6310      * by the specified number of milliseconds. If the event fires again within that time, the original
6311      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6312      * </ul><br>
6313      * <p>
6314      * <b>Combining Options</b><br>
6315      * Using the options argument, it is possible to combine different types of listeners:<br>
6316      * <br>
6317      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6318      * Code:<pre><code>
6319 el.on('click', this.onClick, this, {
6320     single: true,
6321     delay: 100,
6322     stopEvent : true,
6323     forumId: 4
6324 });</code></pre>
6325      * <p>
6326      * <b>Attaching multiple handlers in 1 call</b><br>
6327       * The method also allows for a single argument to be passed which is a config object containing properties
6328      * which specify multiple handlers.
6329      * <p>
6330      * Code:<pre><code>
6331 el.on({
6332     'click' : {
6333         fn: this.onClick
6334         scope: this,
6335         delay: 100
6336     },
6337     'mouseover' : {
6338         fn: this.onMouseOver
6339         scope: this
6340     },
6341     'mouseout' : {
6342         fn: this.onMouseOut
6343         scope: this
6344     }
6345 });</code></pre>
6346      * <p>
6347      * Or a shorthand syntax:<br>
6348      * Code:<pre><code>
6349 el.on({
6350     'click' : this.onClick,
6351     'mouseover' : this.onMouseOver,
6352     'mouseout' : this.onMouseOut
6353     scope: this
6354 });</code></pre>
6355      */
6356         addListener : function(element, eventName, fn, scope, options){
6357             if(typeof eventName == "object"){
6358                 var o = eventName;
6359                 for(var e in o){
6360                     if(propRe.test(e)){
6361                         continue;
6362                     }
6363                     if(typeof o[e] == "function"){
6364                         // shared options
6365                         listen(element, e, o, o[e], o.scope);
6366                     }else{
6367                         // individual options
6368                         listen(element, e, o[e]);
6369                     }
6370                 }
6371                 return;
6372             }
6373             return listen(element, eventName, options, fn, scope);
6374         },
6375         
6376         /**
6377          * Removes an event handler
6378          *
6379          * @param {String/HTMLElement}   element        The id or html element to remove the 
6380          *                             event from
6381          * @param {String}   eventName     The type of event
6382          * @param {Function} fn
6383          * @return {Boolean} True if a listener was actually removed
6384          */
6385         removeListener : function(element, eventName, fn){
6386             return stopListening(element, eventName, fn);
6387         },
6388         
6389         /**
6390          * Fires when the document is ready (before onload and before images are loaded). Can be 
6391          * accessed shorthanded Roo.onReady().
6392          * @param {Function} fn        The method the event invokes
6393          * @param {Object}   scope    An  object that becomes the scope of the handler
6394          * @param {boolean}  options
6395          */
6396         onDocumentReady : function(fn, scope, options){
6397             if(docReadyState){ // if it already fired
6398                 docReadyEvent.addListener(fn, scope, options);
6399                 docReadyEvent.fire();
6400                 docReadyEvent.clearListeners();
6401                 return;
6402             }
6403             if(!docReadyEvent){
6404                 initDocReady();
6405             }
6406             docReadyEvent.addListener(fn, scope, options);
6407         },
6408         
6409         /**
6410          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6411          * @param {Function} fn        The method the event invokes
6412          * @param {Object}   scope    An object that becomes the scope of the handler
6413          * @param {boolean}  options
6414          */
6415         onWindowResize : function(fn, scope, options){
6416             if(!resizeEvent){
6417                 resizeEvent = new Roo.util.Event();
6418                 resizeTask = new Roo.util.DelayedTask(function(){
6419                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6420                 });
6421                 E.on(window, "resize", function(){
6422                     if(Roo.isIE){
6423                         resizeTask.delay(50);
6424                     }else{
6425                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6426                     }
6427                 });
6428             }
6429             resizeEvent.addListener(fn, scope, options);
6430         },
6431
6432         /**
6433          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6434          * @param {Function} fn        The method the event invokes
6435          * @param {Object}   scope    An object that becomes the scope of the handler
6436          * @param {boolean}  options
6437          */
6438         onTextResize : function(fn, scope, options){
6439             if(!textEvent){
6440                 textEvent = new Roo.util.Event();
6441                 var textEl = new Roo.Element(document.createElement('div'));
6442                 textEl.dom.className = 'x-text-resize';
6443                 textEl.dom.innerHTML = 'X';
6444                 textEl.appendTo(document.body);
6445                 textSize = textEl.dom.offsetHeight;
6446                 setInterval(function(){
6447                     if(textEl.dom.offsetHeight != textSize){
6448                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6449                     }
6450                 }, this.textResizeInterval);
6451             }
6452             textEvent.addListener(fn, scope, options);
6453         },
6454
6455         /**
6456          * Removes the passed window resize listener.
6457          * @param {Function} fn        The method the event invokes
6458          * @param {Object}   scope    The scope of handler
6459          */
6460         removeResizeListener : function(fn, scope){
6461             if(resizeEvent){
6462                 resizeEvent.removeListener(fn, scope);
6463             }
6464         },
6465
6466         // private
6467         fireResize : function(){
6468             if(resizeEvent){
6469                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6470             }   
6471         },
6472         /**
6473          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6474          */
6475         ieDeferSrc : false,
6476         /**
6477          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6478          */
6479         textResizeInterval : 50
6480     };
6481     
6482     /**
6483      * Fix for doc tools
6484      * @scopeAlias pub=Roo.EventManager
6485      */
6486     
6487      /**
6488      * Appends an event handler to an element (shorthand for addListener)
6489      * @param {String/HTMLElement}   element        The html element or id to assign the
6490      * @param {String}   eventName The type of event to listen for
6491      * @param {Function} handler The method the event invokes
6492      * @param {Object}   scope (optional) The scope in which to execute the handler
6493      * function. The handler function's "this" context.
6494      * @param {Object}   options (optional) An object containing handler configuration
6495      * properties. This may contain any of the following properties:<ul>
6496      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6497      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6498      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6499      * <li>preventDefault {Boolean} True to prevent the default action</li>
6500      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6501      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6502      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6503      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6504      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6505      * by the specified number of milliseconds. If the event fires again within that time, the original
6506      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6507      * </ul><br>
6508      * <p>
6509      * <b>Combining Options</b><br>
6510      * Using the options argument, it is possible to combine different types of listeners:<br>
6511      * <br>
6512      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6513      * Code:<pre><code>
6514 el.on('click', this.onClick, this, {
6515     single: true,
6516     delay: 100,
6517     stopEvent : true,
6518     forumId: 4
6519 });</code></pre>
6520      * <p>
6521      * <b>Attaching multiple handlers in 1 call</b><br>
6522       * The method also allows for a single argument to be passed which is a config object containing properties
6523      * which specify multiple handlers.
6524      * <p>
6525      * Code:<pre><code>
6526 el.on({
6527     'click' : {
6528         fn: this.onClick
6529         scope: this,
6530         delay: 100
6531     },
6532     'mouseover' : {
6533         fn: this.onMouseOver
6534         scope: this
6535     },
6536     'mouseout' : {
6537         fn: this.onMouseOut
6538         scope: this
6539     }
6540 });</code></pre>
6541      * <p>
6542      * Or a shorthand syntax:<br>
6543      * Code:<pre><code>
6544 el.on({
6545     'click' : this.onClick,
6546     'mouseover' : this.onMouseOver,
6547     'mouseout' : this.onMouseOut
6548     scope: this
6549 });</code></pre>
6550      */
6551     pub.on = pub.addListener;
6552     pub.un = pub.removeListener;
6553
6554     pub.stoppedMouseDownEvent = new Roo.util.Event();
6555     return pub;
6556 }();
6557 /**
6558   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6559   * @param {Function} fn        The method the event invokes
6560   * @param {Object}   scope    An  object that becomes the scope of the handler
6561   * @param {boolean}  override If true, the obj passed in becomes
6562   *                             the execution scope of the listener
6563   * @member Roo
6564   * @method onReady
6565  */
6566 Roo.onReady = Roo.EventManager.onDocumentReady;
6567
6568 Roo.onReady(function(){
6569     var bd = Roo.get(document.body);
6570     if(!bd){ return; }
6571
6572     var cls = [
6573             Roo.isIE ? "roo-ie"
6574             : Roo.isGecko ? "roo-gecko"
6575             : Roo.isOpera ? "roo-opera"
6576             : Roo.isSafari ? "roo-safari" : ""];
6577
6578     if(Roo.isMac){
6579         cls.push("roo-mac");
6580     }
6581     if(Roo.isLinux){
6582         cls.push("roo-linux");
6583     }
6584     if(Roo.isIOS){
6585         cls.push("roo-ios");
6586     }
6587     if(Roo.isTouch){
6588         cls.push("roo-touch");
6589     }
6590     if(Roo.isBorderBox){
6591         cls.push('roo-border-box');
6592     }
6593     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6594         var p = bd.dom.parentNode;
6595         if(p){
6596             p.className += ' roo-strict';
6597         }
6598     }
6599     bd.addClass(cls.join(' '));
6600 });
6601
6602 /**
6603  * @class Roo.EventObject
6604  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6605  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6606  * Example:
6607  * <pre><code>
6608  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6609     e.preventDefault();
6610     var target = e.getTarget();
6611     ...
6612  }
6613  var myDiv = Roo.get("myDiv");
6614  myDiv.on("click", handleClick);
6615  //or
6616  Roo.EventManager.on("myDiv", 'click', handleClick);
6617  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6618  </code></pre>
6619  * @singleton
6620  */
6621 Roo.EventObject = function(){
6622     
6623     var E = Roo.lib.Event;
6624     
6625     // safari keypress events for special keys return bad keycodes
6626     var safariKeys = {
6627         63234 : 37, // left
6628         63235 : 39, // right
6629         63232 : 38, // up
6630         63233 : 40, // down
6631         63276 : 33, // page up
6632         63277 : 34, // page down
6633         63272 : 46, // delete
6634         63273 : 36, // home
6635         63275 : 35  // end
6636     };
6637
6638     // normalize button clicks
6639     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6640                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6641
6642     Roo.EventObjectImpl = function(e){
6643         if(e){
6644             this.setEvent(e.browserEvent || e);
6645         }
6646     };
6647     Roo.EventObjectImpl.prototype = {
6648         /**
6649          * Used to fix doc tools.
6650          * @scope Roo.EventObject.prototype
6651          */
6652             
6653
6654         
6655         
6656         /** The normal browser event */
6657         browserEvent : null,
6658         /** The button pressed in a mouse event */
6659         button : -1,
6660         /** True if the shift key was down during the event */
6661         shiftKey : false,
6662         /** True if the control key was down during the event */
6663         ctrlKey : false,
6664         /** True if the alt key was down during the event */
6665         altKey : false,
6666
6667         /** Key constant 
6668         * @type Number */
6669         BACKSPACE : 8,
6670         /** Key constant 
6671         * @type Number */
6672         TAB : 9,
6673         /** Key constant 
6674         * @type Number */
6675         RETURN : 13,
6676         /** Key constant 
6677         * @type Number */
6678         ENTER : 13,
6679         /** Key constant 
6680         * @type Number */
6681         SHIFT : 16,
6682         /** Key constant 
6683         * @type Number */
6684         CONTROL : 17,
6685         /** Key constant 
6686         * @type Number */
6687         ESC : 27,
6688         /** Key constant 
6689         * @type Number */
6690         SPACE : 32,
6691         /** Key constant 
6692         * @type Number */
6693         PAGEUP : 33,
6694         /** Key constant 
6695         * @type Number */
6696         PAGEDOWN : 34,
6697         /** Key constant 
6698         * @type Number */
6699         END : 35,
6700         /** Key constant 
6701         * @type Number */
6702         HOME : 36,
6703         /** Key constant 
6704         * @type Number */
6705         LEFT : 37,
6706         /** Key constant 
6707         * @type Number */
6708         UP : 38,
6709         /** Key constant 
6710         * @type Number */
6711         RIGHT : 39,
6712         /** Key constant 
6713         * @type Number */
6714         DOWN : 40,
6715         /** Key constant 
6716         * @type Number */
6717         DELETE : 46,
6718         /** Key constant 
6719         * @type Number */
6720         F5 : 116,
6721
6722            /** @private */
6723         setEvent : function(e){
6724             if(e == this || (e && e.browserEvent)){ // already wrapped
6725                 return e;
6726             }
6727             this.browserEvent = e;
6728             if(e){
6729                 // normalize buttons
6730                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6731                 if(e.type == 'click' && this.button == -1){
6732                     this.button = 0;
6733                 }
6734                 this.type = e.type;
6735                 this.shiftKey = e.shiftKey;
6736                 // mac metaKey behaves like ctrlKey
6737                 this.ctrlKey = e.ctrlKey || e.metaKey;
6738                 this.altKey = e.altKey;
6739                 // in getKey these will be normalized for the mac
6740                 this.keyCode = e.keyCode;
6741                 // keyup warnings on firefox.
6742                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6743                 // cache the target for the delayed and or buffered events
6744                 this.target = E.getTarget(e);
6745                 // same for XY
6746                 this.xy = E.getXY(e);
6747             }else{
6748                 this.button = -1;
6749                 this.shiftKey = false;
6750                 this.ctrlKey = false;
6751                 this.altKey = false;
6752                 this.keyCode = 0;
6753                 this.charCode =0;
6754                 this.target = null;
6755                 this.xy = [0, 0];
6756             }
6757             return this;
6758         },
6759
6760         /**
6761          * Stop the event (preventDefault and stopPropagation)
6762          */
6763         stopEvent : function(){
6764             if(this.browserEvent){
6765                 if(this.browserEvent.type == 'mousedown'){
6766                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6767                 }
6768                 E.stopEvent(this.browserEvent);
6769             }
6770         },
6771
6772         /**
6773          * Prevents the browsers default handling of the event.
6774          */
6775         preventDefault : function(){
6776             if(this.browserEvent){
6777                 E.preventDefault(this.browserEvent);
6778             }
6779         },
6780
6781         /** @private */
6782         isNavKeyPress : function(){
6783             var k = this.keyCode;
6784             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6785             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6786         },
6787
6788         isSpecialKey : function(){
6789             var k = this.keyCode;
6790             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6791             (k == 16) || (k == 17) ||
6792             (k >= 18 && k <= 20) ||
6793             (k >= 33 && k <= 35) ||
6794             (k >= 36 && k <= 39) ||
6795             (k >= 44 && k <= 45);
6796         },
6797         /**
6798          * Cancels bubbling of the event.
6799          */
6800         stopPropagation : function(){
6801             if(this.browserEvent){
6802                 if(this.type == 'mousedown'){
6803                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6804                 }
6805                 E.stopPropagation(this.browserEvent);
6806             }
6807         },
6808
6809         /**
6810          * Gets the key code for the event.
6811          * @return {Number}
6812          */
6813         getCharCode : function(){
6814             return this.charCode || this.keyCode;
6815         },
6816
6817         /**
6818          * Returns a normalized keyCode for the event.
6819          * @return {Number} The key code
6820          */
6821         getKey : function(){
6822             var k = this.keyCode || this.charCode;
6823             return Roo.isSafari ? (safariKeys[k] || k) : k;
6824         },
6825
6826         /**
6827          * Gets the x coordinate of the event.
6828          * @return {Number}
6829          */
6830         getPageX : function(){
6831             return this.xy[0];
6832         },
6833
6834         /**
6835          * Gets the y coordinate of the event.
6836          * @return {Number}
6837          */
6838         getPageY : function(){
6839             return this.xy[1];
6840         },
6841
6842         /**
6843          * Gets the time of the event.
6844          * @return {Number}
6845          */
6846         getTime : function(){
6847             if(this.browserEvent){
6848                 return E.getTime(this.browserEvent);
6849             }
6850             return null;
6851         },
6852
6853         /**
6854          * Gets the page coordinates of the event.
6855          * @return {Array} The xy values like [x, y]
6856          */
6857         getXY : function(){
6858             return this.xy;
6859         },
6860
6861         /**
6862          * Gets the target for the event.
6863          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6864          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6865                 search as a number or element (defaults to 10 || document.body)
6866          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6867          * @return {HTMLelement}
6868          */
6869         getTarget : function(selector, maxDepth, returnEl){
6870             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6871         },
6872         /**
6873          * Gets the related target.
6874          * @return {HTMLElement}
6875          */
6876         getRelatedTarget : function(){
6877             if(this.browserEvent){
6878                 return E.getRelatedTarget(this.browserEvent);
6879             }
6880             return null;
6881         },
6882
6883         /**
6884          * Normalizes mouse wheel delta across browsers
6885          * @return {Number} The delta
6886          */
6887         getWheelDelta : function(){
6888             var e = this.browserEvent;
6889             var delta = 0;
6890             if(e.wheelDelta){ /* IE/Opera. */
6891                 delta = e.wheelDelta/120;
6892             }else if(e.detail){ /* Mozilla case. */
6893                 delta = -e.detail/3;
6894             }
6895             return delta;
6896         },
6897
6898         /**
6899          * Returns true if the control, meta, shift or alt key was pressed during this event.
6900          * @return {Boolean}
6901          */
6902         hasModifier : function(){
6903             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6904         },
6905
6906         /**
6907          * Returns true if the target of this event equals el or is a child of el
6908          * @param {String/HTMLElement/Element} el
6909          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6910          * @return {Boolean}
6911          */
6912         within : function(el, related){
6913             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6914             return t && Roo.fly(el).contains(t);
6915         },
6916
6917         getPoint : function(){
6918             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6919         }
6920     };
6921
6922     return new Roo.EventObjectImpl();
6923 }();
6924             
6925     /*
6926  * Based on:
6927  * Ext JS Library 1.1.1
6928  * Copyright(c) 2006-2007, Ext JS, LLC.
6929  *
6930  * Originally Released Under LGPL - original licence link has changed is not relivant.
6931  *
6932  * Fork - LGPL
6933  * <script type="text/javascript">
6934  */
6935
6936  
6937 // was in Composite Element!??!?!
6938  
6939 (function(){
6940     var D = Roo.lib.Dom;
6941     var E = Roo.lib.Event;
6942     var A = Roo.lib.Anim;
6943
6944     // local style camelizing for speed
6945     var propCache = {};
6946     var camelRe = /(-[a-z])/gi;
6947     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6948     var view = document.defaultView;
6949
6950 /**
6951  * @class Roo.Element
6952  * Represents an Element in the DOM.<br><br>
6953  * Usage:<br>
6954 <pre><code>
6955 var el = Roo.get("my-div");
6956
6957 // or with getEl
6958 var el = getEl("my-div");
6959
6960 // or with a DOM element
6961 var el = Roo.get(myDivElement);
6962 </code></pre>
6963  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6964  * each call instead of constructing a new one.<br><br>
6965  * <b>Animations</b><br />
6966  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6967  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6968 <pre>
6969 Option    Default   Description
6970 --------- --------  ---------------------------------------------
6971 duration  .35       The duration of the animation in seconds
6972 easing    easeOut   The YUI easing method
6973 callback  none      A function to execute when the anim completes
6974 scope     this      The scope (this) of the callback function
6975 </pre>
6976 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6977 * manipulate the animation. Here's an example:
6978 <pre><code>
6979 var el = Roo.get("my-div");
6980
6981 // no animation
6982 el.setWidth(100);
6983
6984 // default animation
6985 el.setWidth(100, true);
6986
6987 // animation with some options set
6988 el.setWidth(100, {
6989     duration: 1,
6990     callback: this.foo,
6991     scope: this
6992 });
6993
6994 // using the "anim" property to get the Anim object
6995 var opt = {
6996     duration: 1,
6997     callback: this.foo,
6998     scope: this
6999 };
7000 el.setWidth(100, opt);
7001 ...
7002 if(opt.anim.isAnimated()){
7003     opt.anim.stop();
7004 }
7005 </code></pre>
7006 * <b> Composite (Collections of) Elements</b><br />
7007  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7008  * @constructor Create a new Element directly.
7009  * @param {String/HTMLElement} element
7010  * @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).
7011  */
7012     Roo.Element = function(element, forceNew){
7013         var dom = typeof element == "string" ?
7014                 document.getElementById(element) : element;
7015         if(!dom){ // invalid id/element
7016             return null;
7017         }
7018         var id = dom.id;
7019         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7020             return Roo.Element.cache[id];
7021         }
7022
7023         /**
7024          * The DOM element
7025          * @type HTMLElement
7026          */
7027         this.dom = dom;
7028
7029         /**
7030          * The DOM element ID
7031          * @type String
7032          */
7033         this.id = id || Roo.id(dom);
7034     };
7035
7036     var El = Roo.Element;
7037
7038     El.prototype = {
7039         /**
7040          * The element's default display mode  (defaults to "")
7041          * @type String
7042          */
7043         originalDisplay : "",
7044
7045         visibilityMode : 1,
7046         /**
7047          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7048          * @type String
7049          */
7050         defaultUnit : "px",
7051         
7052         /**
7053          * Sets the element's visibility mode. When setVisible() is called it
7054          * will use this to determine whether to set the visibility or the display property.
7055          * @param visMode Element.VISIBILITY or Element.DISPLAY
7056          * @return {Roo.Element} this
7057          */
7058         setVisibilityMode : function(visMode){
7059             this.visibilityMode = visMode;
7060             return this;
7061         },
7062         /**
7063          * Convenience method for setVisibilityMode(Element.DISPLAY)
7064          * @param {String} display (optional) What to set display to when visible
7065          * @return {Roo.Element} this
7066          */
7067         enableDisplayMode : function(display){
7068             this.setVisibilityMode(El.DISPLAY);
7069             if(typeof display != "undefined") this.originalDisplay = display;
7070             return this;
7071         },
7072
7073         /**
7074          * 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)
7075          * @param {String} selector The simple selector to test
7076          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7077                 search as a number or element (defaults to 10 || document.body)
7078          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7079          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7080          */
7081         findParent : function(simpleSelector, maxDepth, returnEl){
7082             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7083             maxDepth = maxDepth || 50;
7084             if(typeof maxDepth != "number"){
7085                 stopEl = Roo.getDom(maxDepth);
7086                 maxDepth = 10;
7087             }
7088             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7089                 if(dq.is(p, simpleSelector)){
7090                     return returnEl ? Roo.get(p) : p;
7091                 }
7092                 depth++;
7093                 p = p.parentNode;
7094             }
7095             return null;
7096         },
7097
7098
7099         /**
7100          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7101          * @param {String} selector The simple selector to test
7102          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7103                 search as a number or element (defaults to 10 || document.body)
7104          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7105          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7106          */
7107         findParentNode : function(simpleSelector, maxDepth, returnEl){
7108             var p = Roo.fly(this.dom.parentNode, '_internal');
7109             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7110         },
7111
7112         /**
7113          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7114          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7115          * @param {String} selector The simple selector to test
7116          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7117                 search as a number or element (defaults to 10 || document.body)
7118          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7119          */
7120         up : function(simpleSelector, maxDepth){
7121             return this.findParentNode(simpleSelector, maxDepth, true);
7122         },
7123
7124
7125
7126         /**
7127          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7128          * @param {String} selector The simple selector to test
7129          * @return {Boolean} True if this element matches the selector, else false
7130          */
7131         is : function(simpleSelector){
7132             return Roo.DomQuery.is(this.dom, simpleSelector);
7133         },
7134
7135         /**
7136          * Perform animation on this element.
7137          * @param {Object} args The YUI animation control args
7138          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7139          * @param {Function} onComplete (optional) Function to call when animation completes
7140          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7141          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7142          * @return {Roo.Element} this
7143          */
7144         animate : function(args, duration, onComplete, easing, animType){
7145             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7146             return this;
7147         },
7148
7149         /*
7150          * @private Internal animation call
7151          */
7152         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7153             animType = animType || 'run';
7154             opt = opt || {};
7155             var anim = Roo.lib.Anim[animType](
7156                 this.dom, args,
7157                 (opt.duration || defaultDur) || .35,
7158                 (opt.easing || defaultEase) || 'easeOut',
7159                 function(){
7160                     Roo.callback(cb, this);
7161                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7162                 },
7163                 this
7164             );
7165             opt.anim = anim;
7166             return anim;
7167         },
7168
7169         // private legacy anim prep
7170         preanim : function(a, i){
7171             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7172         },
7173
7174         /**
7175          * Removes worthless text nodes
7176          * @param {Boolean} forceReclean (optional) By default the element
7177          * keeps track if it has been cleaned already so
7178          * you can call this over and over. However, if you update the element and
7179          * need to force a reclean, you can pass true.
7180          */
7181         clean : function(forceReclean){
7182             if(this.isCleaned && forceReclean !== true){
7183                 return this;
7184             }
7185             var ns = /\S/;
7186             var d = this.dom, n = d.firstChild, ni = -1;
7187             while(n){
7188                 var nx = n.nextSibling;
7189                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7190                     d.removeChild(n);
7191                 }else{
7192                     n.nodeIndex = ++ni;
7193                 }
7194                 n = nx;
7195             }
7196             this.isCleaned = true;
7197             return this;
7198         },
7199
7200         // private
7201         calcOffsetsTo : function(el){
7202             el = Roo.get(el);
7203             var d = el.dom;
7204             var restorePos = false;
7205             if(el.getStyle('position') == 'static'){
7206                 el.position('relative');
7207                 restorePos = true;
7208             }
7209             var x = 0, y =0;
7210             var op = this.dom;
7211             while(op && op != d && op.tagName != 'HTML'){
7212                 x+= op.offsetLeft;
7213                 y+= op.offsetTop;
7214                 op = op.offsetParent;
7215             }
7216             if(restorePos){
7217                 el.position('static');
7218             }
7219             return [x, y];
7220         },
7221
7222         /**
7223          * Scrolls this element into view within the passed container.
7224          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7225          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7226          * @return {Roo.Element} this
7227          */
7228         scrollIntoView : function(container, hscroll){
7229             var c = Roo.getDom(container) || document.body;
7230             var el = this.dom;
7231
7232             var o = this.calcOffsetsTo(c),
7233                 l = o[0],
7234                 t = o[1],
7235                 b = t+el.offsetHeight,
7236                 r = l+el.offsetWidth;
7237
7238             var ch = c.clientHeight;
7239             var ct = parseInt(c.scrollTop, 10);
7240             var cl = parseInt(c.scrollLeft, 10);
7241             var cb = ct + ch;
7242             var cr = cl + c.clientWidth;
7243
7244             if(t < ct){
7245                 c.scrollTop = t;
7246             }else if(b > cb){
7247                 c.scrollTop = b-ch;
7248             }
7249
7250             if(hscroll !== false){
7251                 if(l < cl){
7252                     c.scrollLeft = l;
7253                 }else if(r > cr){
7254                     c.scrollLeft = r-c.clientWidth;
7255                 }
7256             }
7257             return this;
7258         },
7259
7260         // private
7261         scrollChildIntoView : function(child, hscroll){
7262             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7263         },
7264
7265         /**
7266          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7267          * the new height may not be available immediately.
7268          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7269          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7270          * @param {Function} onComplete (optional) Function to call when animation completes
7271          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7272          * @return {Roo.Element} this
7273          */
7274         autoHeight : function(animate, duration, onComplete, easing){
7275             var oldHeight = this.getHeight();
7276             this.clip();
7277             this.setHeight(1); // force clipping
7278             setTimeout(function(){
7279                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7280                 if(!animate){
7281                     this.setHeight(height);
7282                     this.unclip();
7283                     if(typeof onComplete == "function"){
7284                         onComplete();
7285                     }
7286                 }else{
7287                     this.setHeight(oldHeight); // restore original height
7288                     this.setHeight(height, animate, duration, function(){
7289                         this.unclip();
7290                         if(typeof onComplete == "function") onComplete();
7291                     }.createDelegate(this), easing);
7292                 }
7293             }.createDelegate(this), 0);
7294             return this;
7295         },
7296
7297         /**
7298          * Returns true if this element is an ancestor of the passed element
7299          * @param {HTMLElement/String} el The element to check
7300          * @return {Boolean} True if this element is an ancestor of el, else false
7301          */
7302         contains : function(el){
7303             if(!el){return false;}
7304             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7305         },
7306
7307         /**
7308          * Checks whether the element is currently visible using both visibility and display properties.
7309          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7310          * @return {Boolean} True if the element is currently visible, else false
7311          */
7312         isVisible : function(deep) {
7313             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7314             if(deep !== true || !vis){
7315                 return vis;
7316             }
7317             var p = this.dom.parentNode;
7318             while(p && p.tagName.toLowerCase() != "body"){
7319                 if(!Roo.fly(p, '_isVisible').isVisible()){
7320                     return false;
7321                 }
7322                 p = p.parentNode;
7323             }
7324             return true;
7325         },
7326
7327         /**
7328          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7329          * @param {String} selector The CSS selector
7330          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7331          * @return {CompositeElement/CompositeElementLite} The composite element
7332          */
7333         select : function(selector, unique){
7334             return El.select(selector, unique, this.dom);
7335         },
7336
7337         /**
7338          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7339          * @param {String} selector The CSS selector
7340          * @return {Array} An array of the matched nodes
7341          */
7342         query : function(selector, unique){
7343             return Roo.DomQuery.select(selector, this.dom);
7344         },
7345
7346         /**
7347          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7348          * @param {String} selector The CSS selector
7349          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7350          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7351          */
7352         child : function(selector, returnDom){
7353             var n = Roo.DomQuery.selectNode(selector, this.dom);
7354             return returnDom ? n : Roo.get(n);
7355         },
7356
7357         /**
7358          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7359          * @param {String} selector The CSS selector
7360          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7361          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7362          */
7363         down : function(selector, returnDom){
7364             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7365             return returnDom ? n : Roo.get(n);
7366         },
7367
7368         /**
7369          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7370          * @param {String} group The group the DD object is member of
7371          * @param {Object} config The DD config object
7372          * @param {Object} overrides An object containing methods to override/implement on the DD object
7373          * @return {Roo.dd.DD} The DD object
7374          */
7375         initDD : function(group, config, overrides){
7376             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7377             return Roo.apply(dd, overrides);
7378         },
7379
7380         /**
7381          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7382          * @param {String} group The group the DDProxy object is member of
7383          * @param {Object} config The DDProxy config object
7384          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7385          * @return {Roo.dd.DDProxy} The DDProxy object
7386          */
7387         initDDProxy : function(group, config, overrides){
7388             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7389             return Roo.apply(dd, overrides);
7390         },
7391
7392         /**
7393          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7394          * @param {String} group The group the DDTarget object is member of
7395          * @param {Object} config The DDTarget config object
7396          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7397          * @return {Roo.dd.DDTarget} The DDTarget object
7398          */
7399         initDDTarget : function(group, config, overrides){
7400             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7401             return Roo.apply(dd, overrides);
7402         },
7403
7404         /**
7405          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7406          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7407          * @param {Boolean} visible Whether the element is visible
7408          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7409          * @return {Roo.Element} this
7410          */
7411          setVisible : function(visible, animate){
7412             if(!animate || !A){
7413                 if(this.visibilityMode == El.DISPLAY){
7414                     this.setDisplayed(visible);
7415                 }else{
7416                     this.fixDisplay();
7417                     this.dom.style.visibility = visible ? "visible" : "hidden";
7418                 }
7419             }else{
7420                 // closure for composites
7421                 var dom = this.dom;
7422                 var visMode = this.visibilityMode;
7423                 if(visible){
7424                     this.setOpacity(.01);
7425                     this.setVisible(true);
7426                 }
7427                 this.anim({opacity: { to: (visible?1:0) }},
7428                       this.preanim(arguments, 1),
7429                       null, .35, 'easeIn', function(){
7430                          if(!visible){
7431                              if(visMode == El.DISPLAY){
7432                                  dom.style.display = "none";
7433                              }else{
7434                                  dom.style.visibility = "hidden";
7435                              }
7436                              Roo.get(dom).setOpacity(1);
7437                          }
7438                      });
7439             }
7440             return this;
7441         },
7442
7443         /**
7444          * Returns true if display is not "none"
7445          * @return {Boolean}
7446          */
7447         isDisplayed : function() {
7448             return this.getStyle("display") != "none";
7449         },
7450
7451         /**
7452          * Toggles the element's visibility or display, depending on visibility mode.
7453          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7454          * @return {Roo.Element} this
7455          */
7456         toggle : function(animate){
7457             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7458             return this;
7459         },
7460
7461         /**
7462          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7463          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7464          * @return {Roo.Element} this
7465          */
7466         setDisplayed : function(value) {
7467             if(typeof value == "boolean"){
7468                value = value ? this.originalDisplay : "none";
7469             }
7470             this.setStyle("display", value);
7471             return this;
7472         },
7473
7474         /**
7475          * Tries to focus the element. Any exceptions are caught and ignored.
7476          * @return {Roo.Element} this
7477          */
7478         focus : function() {
7479             try{
7480                 this.dom.focus();
7481             }catch(e){}
7482             return this;
7483         },
7484
7485         /**
7486          * Tries to blur the element. Any exceptions are caught and ignored.
7487          * @return {Roo.Element} this
7488          */
7489         blur : function() {
7490             try{
7491                 this.dom.blur();
7492             }catch(e){}
7493             return this;
7494         },
7495
7496         /**
7497          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7498          * @param {String/Array} className The CSS class to add, or an array of classes
7499          * @return {Roo.Element} this
7500          */
7501         addClass : function(className){
7502             if(className instanceof Array){
7503                 for(var i = 0, len = className.length; i < len; i++) {
7504                     this.addClass(className[i]);
7505                 }
7506             }else{
7507                 if(className && !this.hasClass(className)){
7508                     this.dom.className = this.dom.className + " " + className;
7509                 }
7510             }
7511             return this;
7512         },
7513
7514         /**
7515          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7516          * @param {String/Array} className The CSS class to add, or an array of classes
7517          * @return {Roo.Element} this
7518          */
7519         radioClass : function(className){
7520             var siblings = this.dom.parentNode.childNodes;
7521             for(var i = 0; i < siblings.length; i++) {
7522                 var s = siblings[i];
7523                 if(s.nodeType == 1){
7524                     Roo.get(s).removeClass(className);
7525                 }
7526             }
7527             this.addClass(className);
7528             return this;
7529         },
7530
7531         /**
7532          * Removes one or more CSS classes from the element.
7533          * @param {String/Array} className The CSS class to remove, or an array of classes
7534          * @return {Roo.Element} this
7535          */
7536         removeClass : function(className){
7537             if(!className || !this.dom.className){
7538                 return this;
7539             }
7540             if(className instanceof Array){
7541                 for(var i = 0, len = className.length; i < len; i++) {
7542                     this.removeClass(className[i]);
7543                 }
7544             }else{
7545                 if(this.hasClass(className)){
7546                     var re = this.classReCache[className];
7547                     if (!re) {
7548                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7549                        this.classReCache[className] = re;
7550                     }
7551                     this.dom.className =
7552                         this.dom.className.replace(re, " ");
7553                 }
7554             }
7555             return this;
7556         },
7557
7558         // private
7559         classReCache: {},
7560
7561         /**
7562          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7563          * @param {String} className The CSS class to toggle
7564          * @return {Roo.Element} this
7565          */
7566         toggleClass : function(className){
7567             if(this.hasClass(className)){
7568                 this.removeClass(className);
7569             }else{
7570                 this.addClass(className);
7571             }
7572             return this;
7573         },
7574
7575         /**
7576          * Checks if the specified CSS class exists on this element's DOM node.
7577          * @param {String} className The CSS class to check for
7578          * @return {Boolean} True if the class exists, else false
7579          */
7580         hasClass : function(className){
7581             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7582         },
7583
7584         /**
7585          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7586          * @param {String} oldClassName The CSS class to replace
7587          * @param {String} newClassName The replacement CSS class
7588          * @return {Roo.Element} this
7589          */
7590         replaceClass : function(oldClassName, newClassName){
7591             this.removeClass(oldClassName);
7592             this.addClass(newClassName);
7593             return this;
7594         },
7595
7596         /**
7597          * Returns an object with properties matching the styles requested.
7598          * For example, el.getStyles('color', 'font-size', 'width') might return
7599          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7600          * @param {String} style1 A style name
7601          * @param {String} style2 A style name
7602          * @param {String} etc.
7603          * @return {Object} The style object
7604          */
7605         getStyles : function(){
7606             var a = arguments, len = a.length, r = {};
7607             for(var i = 0; i < len; i++){
7608                 r[a[i]] = this.getStyle(a[i]);
7609             }
7610             return r;
7611         },
7612
7613         /**
7614          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7615          * @param {String} property The style property whose value is returned.
7616          * @return {String} The current value of the style property for this element.
7617          */
7618         getStyle : function(){
7619             return view && view.getComputedStyle ?
7620                 function(prop){
7621                     var el = this.dom, v, cs, camel;
7622                     if(prop == 'float'){
7623                         prop = "cssFloat";
7624                     }
7625                     if(el.style && (v = el.style[prop])){
7626                         return v;
7627                     }
7628                     if(cs = view.getComputedStyle(el, "")){
7629                         if(!(camel = propCache[prop])){
7630                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7631                         }
7632                         return cs[camel];
7633                     }
7634                     return null;
7635                 } :
7636                 function(prop){
7637                     var el = this.dom, v, cs, camel;
7638                     if(prop == 'opacity'){
7639                         if(typeof el.style.filter == 'string'){
7640                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7641                             if(m){
7642                                 var fv = parseFloat(m[1]);
7643                                 if(!isNaN(fv)){
7644                                     return fv ? fv / 100 : 0;
7645                                 }
7646                             }
7647                         }
7648                         return 1;
7649                     }else if(prop == 'float'){
7650                         prop = "styleFloat";
7651                     }
7652                     if(!(camel = propCache[prop])){
7653                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7654                     }
7655                     if(v = el.style[camel]){
7656                         return v;
7657                     }
7658                     if(cs = el.currentStyle){
7659                         return cs[camel];
7660                     }
7661                     return null;
7662                 };
7663         }(),
7664
7665         /**
7666          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7667          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7668          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7669          * @return {Roo.Element} this
7670          */
7671         setStyle : function(prop, value){
7672             if(typeof prop == "string"){
7673                 
7674                 if (prop == 'float') {
7675                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7676                     return this;
7677                 }
7678                 
7679                 var camel;
7680                 if(!(camel = propCache[prop])){
7681                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7682                 }
7683                 
7684                 if(camel == 'opacity') {
7685                     this.setOpacity(value);
7686                 }else{
7687                     this.dom.style[camel] = value;
7688                 }
7689             }else{
7690                 for(var style in prop){
7691                     if(typeof prop[style] != "function"){
7692                        this.setStyle(style, prop[style]);
7693                     }
7694                 }
7695             }
7696             return this;
7697         },
7698
7699         /**
7700          * More flexible version of {@link #setStyle} for setting style properties.
7701          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7702          * a function which returns such a specification.
7703          * @return {Roo.Element} this
7704          */
7705         applyStyles : function(style){
7706             Roo.DomHelper.applyStyles(this.dom, style);
7707             return this;
7708         },
7709
7710         /**
7711           * 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).
7712           * @return {Number} The X position of the element
7713           */
7714         getX : function(){
7715             return D.getX(this.dom);
7716         },
7717
7718         /**
7719           * 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).
7720           * @return {Number} The Y position of the element
7721           */
7722         getY : function(){
7723             return D.getY(this.dom);
7724         },
7725
7726         /**
7727           * 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).
7728           * @return {Array} The XY position of the element
7729           */
7730         getXY : function(){
7731             return D.getXY(this.dom);
7732         },
7733
7734         /**
7735          * 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).
7736          * @param {Number} The X position of the element
7737          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7738          * @return {Roo.Element} this
7739          */
7740         setX : function(x, animate){
7741             if(!animate || !A){
7742                 D.setX(this.dom, x);
7743             }else{
7744                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7745             }
7746             return this;
7747         },
7748
7749         /**
7750          * 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).
7751          * @param {Number} The Y position of the element
7752          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7753          * @return {Roo.Element} this
7754          */
7755         setY : function(y, animate){
7756             if(!animate || !A){
7757                 D.setY(this.dom, y);
7758             }else{
7759                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7760             }
7761             return this;
7762         },
7763
7764         /**
7765          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7766          * @param {String} left The left CSS property value
7767          * @return {Roo.Element} this
7768          */
7769         setLeft : function(left){
7770             this.setStyle("left", this.addUnits(left));
7771             return this;
7772         },
7773
7774         /**
7775          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7776          * @param {String} top The top CSS property value
7777          * @return {Roo.Element} this
7778          */
7779         setTop : function(top){
7780             this.setStyle("top", this.addUnits(top));
7781             return this;
7782         },
7783
7784         /**
7785          * Sets the element's CSS right style.
7786          * @param {String} right The right CSS property value
7787          * @return {Roo.Element} this
7788          */
7789         setRight : function(right){
7790             this.setStyle("right", this.addUnits(right));
7791             return this;
7792         },
7793
7794         /**
7795          * Sets the element's CSS bottom style.
7796          * @param {String} bottom The bottom CSS property value
7797          * @return {Roo.Element} this
7798          */
7799         setBottom : function(bottom){
7800             this.setStyle("bottom", this.addUnits(bottom));
7801             return this;
7802         },
7803
7804         /**
7805          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7806          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7807          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7808          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7809          * @return {Roo.Element} this
7810          */
7811         setXY : function(pos, animate){
7812             if(!animate || !A){
7813                 D.setXY(this.dom, pos);
7814             }else{
7815                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7816             }
7817             return this;
7818         },
7819
7820         /**
7821          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7822          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7823          * @param {Number} x X value for new position (coordinates are page-based)
7824          * @param {Number} y Y value for new position (coordinates are page-based)
7825          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7826          * @return {Roo.Element} this
7827          */
7828         setLocation : function(x, y, animate){
7829             this.setXY([x, y], this.preanim(arguments, 2));
7830             return this;
7831         },
7832
7833         /**
7834          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7835          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7836          * @param {Number} x X value for new position (coordinates are page-based)
7837          * @param {Number} y Y value for new position (coordinates are page-based)
7838          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7839          * @return {Roo.Element} this
7840          */
7841         moveTo : function(x, y, animate){
7842             this.setXY([x, y], this.preanim(arguments, 2));
7843             return this;
7844         },
7845
7846         /**
7847          * Returns the region of the given element.
7848          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7849          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7850          */
7851         getRegion : function(){
7852             return D.getRegion(this.dom);
7853         },
7854
7855         /**
7856          * Returns the offset height of the element
7857          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7858          * @return {Number} The element's height
7859          */
7860         getHeight : function(contentHeight){
7861             var h = this.dom.offsetHeight || 0;
7862             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7863         },
7864
7865         /**
7866          * Returns the offset width of the element
7867          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7868          * @return {Number} The element's width
7869          */
7870         getWidth : function(contentWidth){
7871             var w = this.dom.offsetWidth || 0;
7872             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7873         },
7874
7875         /**
7876          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7877          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7878          * if a height has not been set using CSS.
7879          * @return {Number}
7880          */
7881         getComputedHeight : function(){
7882             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7883             if(!h){
7884                 h = parseInt(this.getStyle('height'), 10) || 0;
7885                 if(!this.isBorderBox()){
7886                     h += this.getFrameWidth('tb');
7887                 }
7888             }
7889             return h;
7890         },
7891
7892         /**
7893          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7894          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7895          * if a width has not been set using CSS.
7896          * @return {Number}
7897          */
7898         getComputedWidth : function(){
7899             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7900             if(!w){
7901                 w = parseInt(this.getStyle('width'), 10) || 0;
7902                 if(!this.isBorderBox()){
7903                     w += this.getFrameWidth('lr');
7904                 }
7905             }
7906             return w;
7907         },
7908
7909         /**
7910          * Returns the size of the element.
7911          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7912          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7913          */
7914         getSize : function(contentSize){
7915             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7916         },
7917
7918         /**
7919          * Returns the width and height of the viewport.
7920          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7921          */
7922         getViewSize : function(){
7923             var d = this.dom, doc = document, aw = 0, ah = 0;
7924             if(d == doc || d == doc.body){
7925                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7926             }else{
7927                 return {
7928                     width : d.clientWidth,
7929                     height: d.clientHeight
7930                 };
7931             }
7932         },
7933
7934         /**
7935          * Returns the value of the "value" attribute
7936          * @param {Boolean} asNumber true to parse the value as a number
7937          * @return {String/Number}
7938          */
7939         getValue : function(asNumber){
7940             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7941         },
7942
7943         // private
7944         adjustWidth : function(width){
7945             if(typeof width == "number"){
7946                 if(this.autoBoxAdjust && !this.isBorderBox()){
7947                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7948                 }
7949                 if(width < 0){
7950                     width = 0;
7951                 }
7952             }
7953             return width;
7954         },
7955
7956         // private
7957         adjustHeight : function(height){
7958             if(typeof height == "number"){
7959                if(this.autoBoxAdjust && !this.isBorderBox()){
7960                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7961                }
7962                if(height < 0){
7963                    height = 0;
7964                }
7965             }
7966             return height;
7967         },
7968
7969         /**
7970          * Set the width of the element
7971          * @param {Number} width The new width
7972          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7973          * @return {Roo.Element} this
7974          */
7975         setWidth : function(width, animate){
7976             width = this.adjustWidth(width);
7977             if(!animate || !A){
7978                 this.dom.style.width = this.addUnits(width);
7979             }else{
7980                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7981             }
7982             return this;
7983         },
7984
7985         /**
7986          * Set the height of the element
7987          * @param {Number} height The new height
7988          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7989          * @return {Roo.Element} this
7990          */
7991          setHeight : function(height, animate){
7992             height = this.adjustHeight(height);
7993             if(!animate || !A){
7994                 this.dom.style.height = this.addUnits(height);
7995             }else{
7996                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7997             }
7998             return this;
7999         },
8000
8001         /**
8002          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8003          * @param {Number} width The new width
8004          * @param {Number} height The new height
8005          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8006          * @return {Roo.Element} this
8007          */
8008          setSize : function(width, height, animate){
8009             if(typeof width == "object"){ // in case of object from getSize()
8010                 height = width.height; width = width.width;
8011             }
8012             width = this.adjustWidth(width); height = this.adjustHeight(height);
8013             if(!animate || !A){
8014                 this.dom.style.width = this.addUnits(width);
8015                 this.dom.style.height = this.addUnits(height);
8016             }else{
8017                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8018             }
8019             return this;
8020         },
8021
8022         /**
8023          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8024          * @param {Number} x X value for new position (coordinates are page-based)
8025          * @param {Number} y Y value for new position (coordinates are page-based)
8026          * @param {Number} width The new width
8027          * @param {Number} height The new height
8028          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8029          * @return {Roo.Element} this
8030          */
8031         setBounds : function(x, y, width, height, animate){
8032             if(!animate || !A){
8033                 this.setSize(width, height);
8034                 this.setLocation(x, y);
8035             }else{
8036                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8037                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8038                               this.preanim(arguments, 4), 'motion');
8039             }
8040             return this;
8041         },
8042
8043         /**
8044          * 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.
8045          * @param {Roo.lib.Region} region The region to fill
8046          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8047          * @return {Roo.Element} this
8048          */
8049         setRegion : function(region, animate){
8050             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8051             return this;
8052         },
8053
8054         /**
8055          * Appends an event handler
8056          *
8057          * @param {String}   eventName     The type of event to append
8058          * @param {Function} fn        The method the event invokes
8059          * @param {Object} scope       (optional) The scope (this object) of the fn
8060          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8061          */
8062         addListener : function(eventName, fn, scope, options){
8063             if (this.dom) {
8064                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8065             }
8066         },
8067
8068         /**
8069          * Removes an event handler from this element
8070          * @param {String} eventName the type of event to remove
8071          * @param {Function} fn the method the event invokes
8072          * @return {Roo.Element} this
8073          */
8074         removeListener : function(eventName, fn){
8075             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8076             return this;
8077         },
8078
8079         /**
8080          * Removes all previous added listeners from this element
8081          * @return {Roo.Element} this
8082          */
8083         removeAllListeners : function(){
8084             E.purgeElement(this.dom);
8085             return this;
8086         },
8087
8088         relayEvent : function(eventName, observable){
8089             this.on(eventName, function(e){
8090                 observable.fireEvent(eventName, e);
8091             });
8092         },
8093
8094         /**
8095          * Set the opacity of the element
8096          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8097          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8098          * @return {Roo.Element} this
8099          */
8100          setOpacity : function(opacity, animate){
8101             if(!animate || !A){
8102                 var s = this.dom.style;
8103                 if(Roo.isIE){
8104                     s.zoom = 1;
8105                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8106                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8107                 }else{
8108                     s.opacity = opacity;
8109                 }
8110             }else{
8111                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8112             }
8113             return this;
8114         },
8115
8116         /**
8117          * Gets the left X coordinate
8118          * @param {Boolean} local True to get the local css position instead of page coordinate
8119          * @return {Number}
8120          */
8121         getLeft : function(local){
8122             if(!local){
8123                 return this.getX();
8124             }else{
8125                 return parseInt(this.getStyle("left"), 10) || 0;
8126             }
8127         },
8128
8129         /**
8130          * Gets the right X coordinate of the element (element X position + element width)
8131          * @param {Boolean} local True to get the local css position instead of page coordinate
8132          * @return {Number}
8133          */
8134         getRight : function(local){
8135             if(!local){
8136                 return this.getX() + this.getWidth();
8137             }else{
8138                 return (this.getLeft(true) + this.getWidth()) || 0;
8139             }
8140         },
8141
8142         /**
8143          * Gets the top Y coordinate
8144          * @param {Boolean} local True to get the local css position instead of page coordinate
8145          * @return {Number}
8146          */
8147         getTop : function(local) {
8148             if(!local){
8149                 return this.getY();
8150             }else{
8151                 return parseInt(this.getStyle("top"), 10) || 0;
8152             }
8153         },
8154
8155         /**
8156          * Gets the bottom Y coordinate of the element (element Y position + element height)
8157          * @param {Boolean} local True to get the local css position instead of page coordinate
8158          * @return {Number}
8159          */
8160         getBottom : function(local){
8161             if(!local){
8162                 return this.getY() + this.getHeight();
8163             }else{
8164                 return (this.getTop(true) + this.getHeight()) || 0;
8165             }
8166         },
8167
8168         /**
8169         * Initializes positioning on this element. If a desired position is not passed, it will make the
8170         * the element positioned relative IF it is not already positioned.
8171         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8172         * @param {Number} zIndex (optional) The zIndex to apply
8173         * @param {Number} x (optional) Set the page X position
8174         * @param {Number} y (optional) Set the page Y position
8175         */
8176         position : function(pos, zIndex, x, y){
8177             if(!pos){
8178                if(this.getStyle('position') == 'static'){
8179                    this.setStyle('position', 'relative');
8180                }
8181             }else{
8182                 this.setStyle("position", pos);
8183             }
8184             if(zIndex){
8185                 this.setStyle("z-index", zIndex);
8186             }
8187             if(x !== undefined && y !== undefined){
8188                 this.setXY([x, y]);
8189             }else if(x !== undefined){
8190                 this.setX(x);
8191             }else if(y !== undefined){
8192                 this.setY(y);
8193             }
8194         },
8195
8196         /**
8197         * Clear positioning back to the default when the document was loaded
8198         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8199         * @return {Roo.Element} this
8200          */
8201         clearPositioning : function(value){
8202             value = value ||'';
8203             this.setStyle({
8204                 "left": value,
8205                 "right": value,
8206                 "top": value,
8207                 "bottom": value,
8208                 "z-index": "",
8209                 "position" : "static"
8210             });
8211             return this;
8212         },
8213
8214         /**
8215         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8216         * snapshot before performing an update and then restoring the element.
8217         * @return {Object}
8218         */
8219         getPositioning : function(){
8220             var l = this.getStyle("left");
8221             var t = this.getStyle("top");
8222             return {
8223                 "position" : this.getStyle("position"),
8224                 "left" : l,
8225                 "right" : l ? "" : this.getStyle("right"),
8226                 "top" : t,
8227                 "bottom" : t ? "" : this.getStyle("bottom"),
8228                 "z-index" : this.getStyle("z-index")
8229             };
8230         },
8231
8232         /**
8233          * Gets the width of the border(s) for the specified side(s)
8234          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8235          * passing lr would get the border (l)eft width + the border (r)ight width.
8236          * @return {Number} The width of the sides passed added together
8237          */
8238         getBorderWidth : function(side){
8239             return this.addStyles(side, El.borders);
8240         },
8241
8242         /**
8243          * Gets the width of the padding(s) for the specified side(s)
8244          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8245          * passing lr would get the padding (l)eft + the padding (r)ight.
8246          * @return {Number} The padding of the sides passed added together
8247          */
8248         getPadding : function(side){
8249             return this.addStyles(side, El.paddings);
8250         },
8251
8252         /**
8253         * Set positioning with an object returned by getPositioning().
8254         * @param {Object} posCfg
8255         * @return {Roo.Element} this
8256          */
8257         setPositioning : function(pc){
8258             this.applyStyles(pc);
8259             if(pc.right == "auto"){
8260                 this.dom.style.right = "";
8261             }
8262             if(pc.bottom == "auto"){
8263                 this.dom.style.bottom = "";
8264             }
8265             return this;
8266         },
8267
8268         // private
8269         fixDisplay : function(){
8270             if(this.getStyle("display") == "none"){
8271                 this.setStyle("visibility", "hidden");
8272                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8273                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8274                     this.setStyle("display", "block");
8275                 }
8276             }
8277         },
8278
8279         /**
8280          * Quick set left and top adding default units
8281          * @param {String} left The left CSS property value
8282          * @param {String} top The top CSS property value
8283          * @return {Roo.Element} this
8284          */
8285          setLeftTop : function(left, top){
8286             this.dom.style.left = this.addUnits(left);
8287             this.dom.style.top = this.addUnits(top);
8288             return this;
8289         },
8290
8291         /**
8292          * Move this element relative to its current position.
8293          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8294          * @param {Number} distance How far to move the element in pixels
8295          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8296          * @return {Roo.Element} this
8297          */
8298          move : function(direction, distance, animate){
8299             var xy = this.getXY();
8300             direction = direction.toLowerCase();
8301             switch(direction){
8302                 case "l":
8303                 case "left":
8304                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8305                     break;
8306                case "r":
8307                case "right":
8308                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8309                     break;
8310                case "t":
8311                case "top":
8312                case "up":
8313                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8314                     break;
8315                case "b":
8316                case "bottom":
8317                case "down":
8318                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8319                     break;
8320             }
8321             return this;
8322         },
8323
8324         /**
8325          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8326          * @return {Roo.Element} this
8327          */
8328         clip : function(){
8329             if(!this.isClipped){
8330                this.isClipped = true;
8331                this.originalClip = {
8332                    "o": this.getStyle("overflow"),
8333                    "x": this.getStyle("overflow-x"),
8334                    "y": this.getStyle("overflow-y")
8335                };
8336                this.setStyle("overflow", "hidden");
8337                this.setStyle("overflow-x", "hidden");
8338                this.setStyle("overflow-y", "hidden");
8339             }
8340             return this;
8341         },
8342
8343         /**
8344          *  Return clipping (overflow) to original clipping before clip() was called
8345          * @return {Roo.Element} this
8346          */
8347         unclip : function(){
8348             if(this.isClipped){
8349                 this.isClipped = false;
8350                 var o = this.originalClip;
8351                 if(o.o){this.setStyle("overflow", o.o);}
8352                 if(o.x){this.setStyle("overflow-x", o.x);}
8353                 if(o.y){this.setStyle("overflow-y", o.y);}
8354             }
8355             return this;
8356         },
8357
8358
8359         /**
8360          * Gets the x,y coordinates specified by the anchor position on the element.
8361          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8362          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8363          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8364          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8365          * @return {Array} [x, y] An array containing the element's x and y coordinates
8366          */
8367         getAnchorXY : function(anchor, local, s){
8368             //Passing a different size is useful for pre-calculating anchors,
8369             //especially for anchored animations that change the el size.
8370
8371             var w, h, vp = false;
8372             if(!s){
8373                 var d = this.dom;
8374                 if(d == document.body || d == document){
8375                     vp = true;
8376                     w = D.getViewWidth(); h = D.getViewHeight();
8377                 }else{
8378                     w = this.getWidth(); h = this.getHeight();
8379                 }
8380             }else{
8381                 w = s.width;  h = s.height;
8382             }
8383             var x = 0, y = 0, r = Math.round;
8384             switch((anchor || "tl").toLowerCase()){
8385                 case "c":
8386                     x = r(w*.5);
8387                     y = r(h*.5);
8388                 break;
8389                 case "t":
8390                     x = r(w*.5);
8391                     y = 0;
8392                 break;
8393                 case "l":
8394                     x = 0;
8395                     y = r(h*.5);
8396                 break;
8397                 case "r":
8398                     x = w;
8399                     y = r(h*.5);
8400                 break;
8401                 case "b":
8402                     x = r(w*.5);
8403                     y = h;
8404                 break;
8405                 case "tl":
8406                     x = 0;
8407                     y = 0;
8408                 break;
8409                 case "bl":
8410                     x = 0;
8411                     y = h;
8412                 break;
8413                 case "br":
8414                     x = w;
8415                     y = h;
8416                 break;
8417                 case "tr":
8418                     x = w;
8419                     y = 0;
8420                 break;
8421             }
8422             if(local === true){
8423                 return [x, y];
8424             }
8425             if(vp){
8426                 var sc = this.getScroll();
8427                 return [x + sc.left, y + sc.top];
8428             }
8429             //Add the element's offset xy
8430             var o = this.getXY();
8431             return [x+o[0], y+o[1]];
8432         },
8433
8434         /**
8435          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8436          * supported position values.
8437          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8438          * @param {String} position The position to align to.
8439          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8440          * @return {Array} [x, y]
8441          */
8442         getAlignToXY : function(el, p, o){
8443             el = Roo.get(el);
8444             var d = this.dom;
8445             if(!el.dom){
8446                 throw "Element.alignTo with an element that doesn't exist";
8447             }
8448             var c = false; //constrain to viewport
8449             var p1 = "", p2 = "";
8450             o = o || [0,0];
8451
8452             if(!p){
8453                 p = "tl-bl";
8454             }else if(p == "?"){
8455                 p = "tl-bl?";
8456             }else if(p.indexOf("-") == -1){
8457                 p = "tl-" + p;
8458             }
8459             p = p.toLowerCase();
8460             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8461             if(!m){
8462                throw "Element.alignTo with an invalid alignment " + p;
8463             }
8464             p1 = m[1]; p2 = m[2]; c = !!m[3];
8465
8466             //Subtract the aligned el's internal xy from the target's offset xy
8467             //plus custom offset to get the aligned el's new offset xy
8468             var a1 = this.getAnchorXY(p1, true);
8469             var a2 = el.getAnchorXY(p2, false);
8470             var x = a2[0] - a1[0] + o[0];
8471             var y = a2[1] - a1[1] + o[1];
8472             if(c){
8473                 //constrain the aligned el to viewport if necessary
8474                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8475                 // 5px of margin for ie
8476                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8477
8478                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8479                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8480                 //otherwise swap the aligned el to the opposite border of the target.
8481                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8482                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8483                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8484                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8485
8486                var doc = document;
8487                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8488                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8489
8490                if((x+w) > dw + scrollX){
8491                     x = swapX ? r.left-w : dw+scrollX-w;
8492                 }
8493                if(x < scrollX){
8494                    x = swapX ? r.right : scrollX;
8495                }
8496                if((y+h) > dh + scrollY){
8497                     y = swapY ? r.top-h : dh+scrollY-h;
8498                 }
8499                if (y < scrollY){
8500                    y = swapY ? r.bottom : scrollY;
8501                }
8502             }
8503             return [x,y];
8504         },
8505
8506         // private
8507         getConstrainToXY : function(){
8508             var os = {top:0, left:0, bottom:0, right: 0};
8509
8510             return function(el, local, offsets, proposedXY){
8511                 el = Roo.get(el);
8512                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8513
8514                 var vw, vh, vx = 0, vy = 0;
8515                 if(el.dom == document.body || el.dom == document){
8516                     vw = Roo.lib.Dom.getViewWidth();
8517                     vh = Roo.lib.Dom.getViewHeight();
8518                 }else{
8519                     vw = el.dom.clientWidth;
8520                     vh = el.dom.clientHeight;
8521                     if(!local){
8522                         var vxy = el.getXY();
8523                         vx = vxy[0];
8524                         vy = vxy[1];
8525                     }
8526                 }
8527
8528                 var s = el.getScroll();
8529
8530                 vx += offsets.left + s.left;
8531                 vy += offsets.top + s.top;
8532
8533                 vw -= offsets.right;
8534                 vh -= offsets.bottom;
8535
8536                 var vr = vx+vw;
8537                 var vb = vy+vh;
8538
8539                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8540                 var x = xy[0], y = xy[1];
8541                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8542
8543                 // only move it if it needs it
8544                 var moved = false;
8545
8546                 // first validate right/bottom
8547                 if((x + w) > vr){
8548                     x = vr - w;
8549                     moved = true;
8550                 }
8551                 if((y + h) > vb){
8552                     y = vb - h;
8553                     moved = true;
8554                 }
8555                 // then make sure top/left isn't negative
8556                 if(x < vx){
8557                     x = vx;
8558                     moved = true;
8559                 }
8560                 if(y < vy){
8561                     y = vy;
8562                     moved = true;
8563                 }
8564                 return moved ? [x, y] : false;
8565             };
8566         }(),
8567
8568         // private
8569         adjustForConstraints : function(xy, parent, offsets){
8570             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8571         },
8572
8573         /**
8574          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8575          * document it aligns it to the viewport.
8576          * The position parameter is optional, and can be specified in any one of the following formats:
8577          * <ul>
8578          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8579          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8580          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8581          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8582          *   <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
8583          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8584          * </ul>
8585          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8586          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8587          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8588          * that specified in order to enforce the viewport constraints.
8589          * Following are all of the supported anchor positions:
8590     <pre>
8591     Value  Description
8592     -----  -----------------------------
8593     tl     The top left corner (default)
8594     t      The center of the top edge
8595     tr     The top right corner
8596     l      The center of the left edge
8597     c      In the center of the element
8598     r      The center of the right edge
8599     bl     The bottom left corner
8600     b      The center of the bottom edge
8601     br     The bottom right corner
8602     </pre>
8603     Example Usage:
8604     <pre><code>
8605     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8606     el.alignTo("other-el");
8607
8608     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8609     el.alignTo("other-el", "tr?");
8610
8611     // align the bottom right corner of el with the center left edge of other-el
8612     el.alignTo("other-el", "br-l?");
8613
8614     // align the center of el with the bottom left corner of other-el and
8615     // adjust the x position by -6 pixels (and the y position by 0)
8616     el.alignTo("other-el", "c-bl", [-6, 0]);
8617     </code></pre>
8618          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8619          * @param {String} position The position to align to.
8620          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8621          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8622          * @return {Roo.Element} this
8623          */
8624         alignTo : function(element, position, offsets, animate){
8625             var xy = this.getAlignToXY(element, position, offsets);
8626             this.setXY(xy, this.preanim(arguments, 3));
8627             return this;
8628         },
8629
8630         /**
8631          * Anchors an element to another element and realigns it when the window is resized.
8632          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8633          * @param {String} position The position to align to.
8634          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8635          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8636          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8637          * is a number, it is used as the buffer delay (defaults to 50ms).
8638          * @param {Function} callback The function to call after the animation finishes
8639          * @return {Roo.Element} this
8640          */
8641         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8642             var action = function(){
8643                 this.alignTo(el, alignment, offsets, animate);
8644                 Roo.callback(callback, this);
8645             };
8646             Roo.EventManager.onWindowResize(action, this);
8647             var tm = typeof monitorScroll;
8648             if(tm != 'undefined'){
8649                 Roo.EventManager.on(window, 'scroll', action, this,
8650                     {buffer: tm == 'number' ? monitorScroll : 50});
8651             }
8652             action.call(this); // align immediately
8653             return this;
8654         },
8655         /**
8656          * Clears any opacity settings from this element. Required in some cases for IE.
8657          * @return {Roo.Element} this
8658          */
8659         clearOpacity : function(){
8660             if (window.ActiveXObject) {
8661                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8662                     this.dom.style.filter = "";
8663                 }
8664             } else {
8665                 this.dom.style.opacity = "";
8666                 this.dom.style["-moz-opacity"] = "";
8667                 this.dom.style["-khtml-opacity"] = "";
8668             }
8669             return this;
8670         },
8671
8672         /**
8673          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8674          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8675          * @return {Roo.Element} this
8676          */
8677         hide : function(animate){
8678             this.setVisible(false, this.preanim(arguments, 0));
8679             return this;
8680         },
8681
8682         /**
8683         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8684         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8685          * @return {Roo.Element} this
8686          */
8687         show : function(animate){
8688             this.setVisible(true, this.preanim(arguments, 0));
8689             return this;
8690         },
8691
8692         /**
8693          * @private Test if size has a unit, otherwise appends the default
8694          */
8695         addUnits : function(size){
8696             return Roo.Element.addUnits(size, this.defaultUnit);
8697         },
8698
8699         /**
8700          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8701          * @return {Roo.Element} this
8702          */
8703         beginMeasure : function(){
8704             var el = this.dom;
8705             if(el.offsetWidth || el.offsetHeight){
8706                 return this; // offsets work already
8707             }
8708             var changed = [];
8709             var p = this.dom, b = document.body; // start with this element
8710             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8711                 var pe = Roo.get(p);
8712                 if(pe.getStyle('display') == 'none'){
8713                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8714                     p.style.visibility = "hidden";
8715                     p.style.display = "block";
8716                 }
8717                 p = p.parentNode;
8718             }
8719             this._measureChanged = changed;
8720             return this;
8721
8722         },
8723
8724         /**
8725          * Restores displays to before beginMeasure was called
8726          * @return {Roo.Element} this
8727          */
8728         endMeasure : function(){
8729             var changed = this._measureChanged;
8730             if(changed){
8731                 for(var i = 0, len = changed.length; i < len; i++) {
8732                     var r = changed[i];
8733                     r.el.style.visibility = r.visibility;
8734                     r.el.style.display = "none";
8735                 }
8736                 this._measureChanged = null;
8737             }
8738             return this;
8739         },
8740
8741         /**
8742         * Update the innerHTML of this element, optionally searching for and processing scripts
8743         * @param {String} html The new HTML
8744         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8745         * @param {Function} callback For async script loading you can be noticed when the update completes
8746         * @return {Roo.Element} this
8747          */
8748         update : function(html, loadScripts, callback){
8749             if(typeof html == "undefined"){
8750                 html = "";
8751             }
8752             if(loadScripts !== true){
8753                 this.dom.innerHTML = html;
8754                 if(typeof callback == "function"){
8755                     callback();
8756                 }
8757                 return this;
8758             }
8759             var id = Roo.id();
8760             var dom = this.dom;
8761
8762             html += '<span id="' + id + '"></span>';
8763
8764             E.onAvailable(id, function(){
8765                 var hd = document.getElementsByTagName("head")[0];
8766                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8767                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8768                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8769
8770                 var match;
8771                 while(match = re.exec(html)){
8772                     var attrs = match[1];
8773                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8774                     if(srcMatch && srcMatch[2]){
8775                        var s = document.createElement("script");
8776                        s.src = srcMatch[2];
8777                        var typeMatch = attrs.match(typeRe);
8778                        if(typeMatch && typeMatch[2]){
8779                            s.type = typeMatch[2];
8780                        }
8781                        hd.appendChild(s);
8782                     }else if(match[2] && match[2].length > 0){
8783                         if(window.execScript) {
8784                            window.execScript(match[2]);
8785                         } else {
8786                             /**
8787                              * eval:var:id
8788                              * eval:var:dom
8789                              * eval:var:html
8790                              * 
8791                              */
8792                            window.eval(match[2]);
8793                         }
8794                     }
8795                 }
8796                 var el = document.getElementById(id);
8797                 if(el){el.parentNode.removeChild(el);}
8798                 if(typeof callback == "function"){
8799                     callback();
8800                 }
8801             });
8802             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8803             return this;
8804         },
8805
8806         /**
8807          * Direct access to the UpdateManager update() method (takes the same parameters).
8808          * @param {String/Function} url The url for this request or a function to call to get the url
8809          * @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}
8810          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8811          * @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.
8812          * @return {Roo.Element} this
8813          */
8814         load : function(){
8815             var um = this.getUpdateManager();
8816             um.update.apply(um, arguments);
8817             return this;
8818         },
8819
8820         /**
8821         * Gets this element's UpdateManager
8822         * @return {Roo.UpdateManager} The UpdateManager
8823         */
8824         getUpdateManager : function(){
8825             if(!this.updateManager){
8826                 this.updateManager = new Roo.UpdateManager(this);
8827             }
8828             return this.updateManager;
8829         },
8830
8831         /**
8832          * Disables text selection for this element (normalized across browsers)
8833          * @return {Roo.Element} this
8834          */
8835         unselectable : function(){
8836             this.dom.unselectable = "on";
8837             this.swallowEvent("selectstart", true);
8838             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8839             this.addClass("x-unselectable");
8840             return this;
8841         },
8842
8843         /**
8844         * Calculates the x, y to center this element on the screen
8845         * @return {Array} The x, y values [x, y]
8846         */
8847         getCenterXY : function(){
8848             return this.getAlignToXY(document, 'c-c');
8849         },
8850
8851         /**
8852         * Centers the Element in either the viewport, or another Element.
8853         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8854         */
8855         center : function(centerIn){
8856             this.alignTo(centerIn || document, 'c-c');
8857             return this;
8858         },
8859
8860         /**
8861          * Tests various css rules/browsers to determine if this element uses a border box
8862          * @return {Boolean}
8863          */
8864         isBorderBox : function(){
8865             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8866         },
8867
8868         /**
8869          * Return a box {x, y, width, height} that can be used to set another elements
8870          * size/location to match this element.
8871          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8872          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8873          * @return {Object} box An object in the format {x, y, width, height}
8874          */
8875         getBox : function(contentBox, local){
8876             var xy;
8877             if(!local){
8878                 xy = this.getXY();
8879             }else{
8880                 var left = parseInt(this.getStyle("left"), 10) || 0;
8881                 var top = parseInt(this.getStyle("top"), 10) || 0;
8882                 xy = [left, top];
8883             }
8884             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8885             if(!contentBox){
8886                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8887             }else{
8888                 var l = this.getBorderWidth("l")+this.getPadding("l");
8889                 var r = this.getBorderWidth("r")+this.getPadding("r");
8890                 var t = this.getBorderWidth("t")+this.getPadding("t");
8891                 var b = this.getBorderWidth("b")+this.getPadding("b");
8892                 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)};
8893             }
8894             bx.right = bx.x + bx.width;
8895             bx.bottom = bx.y + bx.height;
8896             return bx;
8897         },
8898
8899         /**
8900          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8901          for more information about the sides.
8902          * @param {String} sides
8903          * @return {Number}
8904          */
8905         getFrameWidth : function(sides, onlyContentBox){
8906             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8907         },
8908
8909         /**
8910          * 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.
8911          * @param {Object} box The box to fill {x, y, width, height}
8912          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8913          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8914          * @return {Roo.Element} this
8915          */
8916         setBox : function(box, adjust, animate){
8917             var w = box.width, h = box.height;
8918             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8919                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8920                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8921             }
8922             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8923             return this;
8924         },
8925
8926         /**
8927          * Forces the browser to repaint this element
8928          * @return {Roo.Element} this
8929          */
8930          repaint : function(){
8931             var dom = this.dom;
8932             this.addClass("x-repaint");
8933             setTimeout(function(){
8934                 Roo.get(dom).removeClass("x-repaint");
8935             }, 1);
8936             return this;
8937         },
8938
8939         /**
8940          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8941          * then it returns the calculated width of the sides (see getPadding)
8942          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8943          * @return {Object/Number}
8944          */
8945         getMargins : function(side){
8946             if(!side){
8947                 return {
8948                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8949                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8950                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8951                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8952                 };
8953             }else{
8954                 return this.addStyles(side, El.margins);
8955              }
8956         },
8957
8958         // private
8959         addStyles : function(sides, styles){
8960             var val = 0, v, w;
8961             for(var i = 0, len = sides.length; i < len; i++){
8962                 v = this.getStyle(styles[sides.charAt(i)]);
8963                 if(v){
8964                      w = parseInt(v, 10);
8965                      if(w){ val += w; }
8966                 }
8967             }
8968             return val;
8969         },
8970
8971         /**
8972          * Creates a proxy element of this element
8973          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8974          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8975          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8976          * @return {Roo.Element} The new proxy element
8977          */
8978         createProxy : function(config, renderTo, matchBox){
8979             if(renderTo){
8980                 renderTo = Roo.getDom(renderTo);
8981             }else{
8982                 renderTo = document.body;
8983             }
8984             config = typeof config == "object" ?
8985                 config : {tag : "div", cls: config};
8986             var proxy = Roo.DomHelper.append(renderTo, config, true);
8987             if(matchBox){
8988                proxy.setBox(this.getBox());
8989             }
8990             return proxy;
8991         },
8992
8993         /**
8994          * Puts a mask over this element to disable user interaction. Requires core.css.
8995          * This method can only be applied to elements which accept child nodes.
8996          * @param {String} msg (optional) A message to display in the mask
8997          * @param {String} msgCls (optional) A css class to apply to the msg element
8998          * @return {Element} The mask  element
8999          */
9000         mask : function(msg, msgCls)
9001         {
9002             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9003                 this.setStyle("position", "relative");
9004             }
9005             if(!this._mask){
9006                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9007             }
9008             this.addClass("x-masked");
9009             this._mask.setDisplayed(true);
9010             
9011             // we wander
9012             var z = 0;
9013             var dom = this.dom;
9014             while (dom && dom.style) {
9015                 if (!isNaN(parseInt(dom.style.zIndex))) {
9016                     z = Math.max(z, parseInt(dom.style.zIndex));
9017                 }
9018                 dom = dom.parentNode;
9019             }
9020             // if we are masking the body - then it hides everything..
9021             if (this.dom == document.body) {
9022                 z = 1000000;
9023                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9024                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9025             }
9026            
9027             if(typeof msg == 'string'){
9028                 if(!this._maskMsg){
9029                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9030                 }
9031                 var mm = this._maskMsg;
9032                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9033                 if (mm.dom.firstChild) { // weird IE issue?
9034                     mm.dom.firstChild.innerHTML = msg;
9035                 }
9036                 mm.setDisplayed(true);
9037                 mm.center(this);
9038                 mm.setStyle('z-index', z + 102);
9039             }
9040             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9041                 this._mask.setHeight(this.getHeight());
9042             }
9043             this._mask.setStyle('z-index', z + 100);
9044             
9045             return this._mask;
9046         },
9047
9048         /**
9049          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9050          * it is cached for reuse.
9051          */
9052         unmask : function(removeEl){
9053             if(this._mask){
9054                 if(removeEl === true){
9055                     this._mask.remove();
9056                     delete this._mask;
9057                     if(this._maskMsg){
9058                         this._maskMsg.remove();
9059                         delete this._maskMsg;
9060                     }
9061                 }else{
9062                     this._mask.setDisplayed(false);
9063                     if(this._maskMsg){
9064                         this._maskMsg.setDisplayed(false);
9065                     }
9066                 }
9067             }
9068             this.removeClass("x-masked");
9069         },
9070
9071         /**
9072          * Returns true if this element is masked
9073          * @return {Boolean}
9074          */
9075         isMasked : function(){
9076             return this._mask && this._mask.isVisible();
9077         },
9078
9079         /**
9080          * Creates an iframe shim for this element to keep selects and other windowed objects from
9081          * showing through.
9082          * @return {Roo.Element} The new shim element
9083          */
9084         createShim : function(){
9085             var el = document.createElement('iframe');
9086             el.frameBorder = 'no';
9087             el.className = 'roo-shim';
9088             if(Roo.isIE && Roo.isSecure){
9089                 el.src = Roo.SSL_SECURE_URL;
9090             }
9091             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9092             shim.autoBoxAdjust = false;
9093             return shim;
9094         },
9095
9096         /**
9097          * Removes this element from the DOM and deletes it from the cache
9098          */
9099         remove : function(){
9100             if(this.dom.parentNode){
9101                 this.dom.parentNode.removeChild(this.dom);
9102             }
9103             delete El.cache[this.dom.id];
9104         },
9105
9106         /**
9107          * Sets up event handlers to add and remove a css class when the mouse is over this element
9108          * @param {String} className
9109          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9110          * mouseout events for children elements
9111          * @return {Roo.Element} this
9112          */
9113         addClassOnOver : function(className, preventFlicker){
9114             this.on("mouseover", function(){
9115                 Roo.fly(this, '_internal').addClass(className);
9116             }, this.dom);
9117             var removeFn = function(e){
9118                 if(preventFlicker !== true || !e.within(this, true)){
9119                     Roo.fly(this, '_internal').removeClass(className);
9120                 }
9121             };
9122             this.on("mouseout", removeFn, this.dom);
9123             return this;
9124         },
9125
9126         /**
9127          * Sets up event handlers to add and remove a css class when this element has the focus
9128          * @param {String} className
9129          * @return {Roo.Element} this
9130          */
9131         addClassOnFocus : function(className){
9132             this.on("focus", function(){
9133                 Roo.fly(this, '_internal').addClass(className);
9134             }, this.dom);
9135             this.on("blur", function(){
9136                 Roo.fly(this, '_internal').removeClass(className);
9137             }, this.dom);
9138             return this;
9139         },
9140         /**
9141          * 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)
9142          * @param {String} className
9143          * @return {Roo.Element} this
9144          */
9145         addClassOnClick : function(className){
9146             var dom = this.dom;
9147             this.on("mousedown", function(){
9148                 Roo.fly(dom, '_internal').addClass(className);
9149                 var d = Roo.get(document);
9150                 var fn = function(){
9151                     Roo.fly(dom, '_internal').removeClass(className);
9152                     d.removeListener("mouseup", fn);
9153                 };
9154                 d.on("mouseup", fn);
9155             });
9156             return this;
9157         },
9158
9159         /**
9160          * Stops the specified event from bubbling and optionally prevents the default action
9161          * @param {String} eventName
9162          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9163          * @return {Roo.Element} this
9164          */
9165         swallowEvent : function(eventName, preventDefault){
9166             var fn = function(e){
9167                 e.stopPropagation();
9168                 if(preventDefault){
9169                     e.preventDefault();
9170                 }
9171             };
9172             if(eventName instanceof Array){
9173                 for(var i = 0, len = eventName.length; i < len; i++){
9174                      this.on(eventName[i], fn);
9175                 }
9176                 return this;
9177             }
9178             this.on(eventName, fn);
9179             return this;
9180         },
9181
9182         /**
9183          * @private
9184          */
9185       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9186
9187         /**
9188          * Sizes this element to its parent element's dimensions performing
9189          * neccessary box adjustments.
9190          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9191          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9192          * @return {Roo.Element} this
9193          */
9194         fitToParent : function(monitorResize, targetParent) {
9195           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9196           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9197           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9198             return;
9199           }
9200           var p = Roo.get(targetParent || this.dom.parentNode);
9201           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9202           if (monitorResize === true) {
9203             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9204             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9205           }
9206           return this;
9207         },
9208
9209         /**
9210          * Gets the next sibling, skipping text nodes
9211          * @return {HTMLElement} The next sibling or null
9212          */
9213         getNextSibling : function(){
9214             var n = this.dom.nextSibling;
9215             while(n && n.nodeType != 1){
9216                 n = n.nextSibling;
9217             }
9218             return n;
9219         },
9220
9221         /**
9222          * Gets the previous sibling, skipping text nodes
9223          * @return {HTMLElement} The previous sibling or null
9224          */
9225         getPrevSibling : function(){
9226             var n = this.dom.previousSibling;
9227             while(n && n.nodeType != 1){
9228                 n = n.previousSibling;
9229             }
9230             return n;
9231         },
9232
9233
9234         /**
9235          * Appends the passed element(s) to this element
9236          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9237          * @return {Roo.Element} this
9238          */
9239         appendChild: function(el){
9240             el = Roo.get(el);
9241             el.appendTo(this);
9242             return this;
9243         },
9244
9245         /**
9246          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9247          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9248          * automatically generated with the specified attributes.
9249          * @param {HTMLElement} insertBefore (optional) a child element of this element
9250          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9251          * @return {Roo.Element} The new child element
9252          */
9253         createChild: function(config, insertBefore, returnDom){
9254             config = config || {tag:'div'};
9255             if(insertBefore){
9256                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9257             }
9258             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9259         },
9260
9261         /**
9262          * Appends this element to the passed element
9263          * @param {String/HTMLElement/Element} el The new parent element
9264          * @return {Roo.Element} this
9265          */
9266         appendTo: function(el){
9267             el = Roo.getDom(el);
9268             el.appendChild(this.dom);
9269             return this;
9270         },
9271
9272         /**
9273          * Inserts this element before the passed element in the DOM
9274          * @param {String/HTMLElement/Element} el The element to insert before
9275          * @return {Roo.Element} this
9276          */
9277         insertBefore: function(el){
9278             el = Roo.getDom(el);
9279             el.parentNode.insertBefore(this.dom, el);
9280             return this;
9281         },
9282
9283         /**
9284          * Inserts this element after the passed element in the DOM
9285          * @param {String/HTMLElement/Element} el The element to insert after
9286          * @return {Roo.Element} this
9287          */
9288         insertAfter: function(el){
9289             el = Roo.getDom(el);
9290             el.parentNode.insertBefore(this.dom, el.nextSibling);
9291             return this;
9292         },
9293
9294         /**
9295          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9296          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9297          * @return {Roo.Element} The new child
9298          */
9299         insertFirst: function(el, returnDom){
9300             el = el || {};
9301             if(typeof el == 'object' && !el.nodeType){ // dh config
9302                 return this.createChild(el, this.dom.firstChild, returnDom);
9303             }else{
9304                 el = Roo.getDom(el);
9305                 this.dom.insertBefore(el, this.dom.firstChild);
9306                 return !returnDom ? Roo.get(el) : el;
9307             }
9308         },
9309
9310         /**
9311          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9312          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9313          * @param {String} where (optional) 'before' or 'after' defaults to before
9314          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9315          * @return {Roo.Element} the inserted Element
9316          */
9317         insertSibling: function(el, where, returnDom){
9318             where = where ? where.toLowerCase() : 'before';
9319             el = el || {};
9320             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9321
9322             if(typeof el == 'object' && !el.nodeType){ // dh config
9323                 if(where == 'after' && !this.dom.nextSibling){
9324                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9325                 }else{
9326                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9327                 }
9328
9329             }else{
9330                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9331                             where == 'before' ? this.dom : this.dom.nextSibling);
9332                 if(!returnDom){
9333                     rt = Roo.get(rt);
9334                 }
9335             }
9336             return rt;
9337         },
9338
9339         /**
9340          * Creates and wraps this element with another element
9341          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9342          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9343          * @return {HTMLElement/Element} The newly created wrapper element
9344          */
9345         wrap: function(config, returnDom){
9346             if(!config){
9347                 config = {tag: "div"};
9348             }
9349             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9350             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9351             return newEl;
9352         },
9353
9354         /**
9355          * Replaces the passed element with this element
9356          * @param {String/HTMLElement/Element} el The element to replace
9357          * @return {Roo.Element} this
9358          */
9359         replace: function(el){
9360             el = Roo.get(el);
9361             this.insertBefore(el);
9362             el.remove();
9363             return this;
9364         },
9365
9366         /**
9367          * Inserts an html fragment into this element
9368          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9369          * @param {String} html The HTML fragment
9370          * @param {Boolean} returnEl True to return an Roo.Element
9371          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9372          */
9373         insertHtml : function(where, html, returnEl){
9374             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9375             return returnEl ? Roo.get(el) : el;
9376         },
9377
9378         /**
9379          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9380          * @param {Object} o The object with the attributes
9381          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9382          * @return {Roo.Element} this
9383          */
9384         set : function(o, useSet){
9385             var el = this.dom;
9386             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9387             for(var attr in o){
9388                 if(attr == "style" || typeof o[attr] == "function") continue;
9389                 if(attr=="cls"){
9390                     el.className = o["cls"];
9391                 }else{
9392                     if(useSet) el.setAttribute(attr, o[attr]);
9393                     else el[attr] = o[attr];
9394                 }
9395             }
9396             if(o.style){
9397                 Roo.DomHelper.applyStyles(el, o.style);
9398             }
9399             return this;
9400         },
9401
9402         /**
9403          * Convenience method for constructing a KeyMap
9404          * @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:
9405          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9406          * @param {Function} fn The function to call
9407          * @param {Object} scope (optional) The scope of the function
9408          * @return {Roo.KeyMap} The KeyMap created
9409          */
9410         addKeyListener : function(key, fn, scope){
9411             var config;
9412             if(typeof key != "object" || key instanceof Array){
9413                 config = {
9414                     key: key,
9415                     fn: fn,
9416                     scope: scope
9417                 };
9418             }else{
9419                 config = {
9420                     key : key.key,
9421                     shift : key.shift,
9422                     ctrl : key.ctrl,
9423                     alt : key.alt,
9424                     fn: fn,
9425                     scope: scope
9426                 };
9427             }
9428             return new Roo.KeyMap(this, config);
9429         },
9430
9431         /**
9432          * Creates a KeyMap for this element
9433          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9434          * @return {Roo.KeyMap} The KeyMap created
9435          */
9436         addKeyMap : function(config){
9437             return new Roo.KeyMap(this, config);
9438         },
9439
9440         /**
9441          * Returns true if this element is scrollable.
9442          * @return {Boolean}
9443          */
9444          isScrollable : function(){
9445             var dom = this.dom;
9446             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9447         },
9448
9449         /**
9450          * 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().
9451          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9452          * @param {Number} value The new scroll value
9453          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9454          * @return {Element} this
9455          */
9456
9457         scrollTo : function(side, value, animate){
9458             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9459             if(!animate || !A){
9460                 this.dom[prop] = value;
9461             }else{
9462                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9463                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9464             }
9465             return this;
9466         },
9467
9468         /**
9469          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9470          * within this element's scrollable range.
9471          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9472          * @param {Number} distance How far to scroll the element in pixels
9473          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9474          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9475          * was scrolled as far as it could go.
9476          */
9477          scroll : function(direction, distance, animate){
9478              if(!this.isScrollable()){
9479                  return;
9480              }
9481              var el = this.dom;
9482              var l = el.scrollLeft, t = el.scrollTop;
9483              var w = el.scrollWidth, h = el.scrollHeight;
9484              var cw = el.clientWidth, ch = el.clientHeight;
9485              direction = direction.toLowerCase();
9486              var scrolled = false;
9487              var a = this.preanim(arguments, 2);
9488              switch(direction){
9489                  case "l":
9490                  case "left":
9491                      if(w - l > cw){
9492                          var v = Math.min(l + distance, w-cw);
9493                          this.scrollTo("left", v, a);
9494                          scrolled = true;
9495                      }
9496                      break;
9497                 case "r":
9498                 case "right":
9499                      if(l > 0){
9500                          var v = Math.max(l - distance, 0);
9501                          this.scrollTo("left", v, a);
9502                          scrolled = true;
9503                      }
9504                      break;
9505                 case "t":
9506                 case "top":
9507                 case "up":
9508                      if(t > 0){
9509                          var v = Math.max(t - distance, 0);
9510                          this.scrollTo("top", v, a);
9511                          scrolled = true;
9512                      }
9513                      break;
9514                 case "b":
9515                 case "bottom":
9516                 case "down":
9517                      if(h - t > ch){
9518                          var v = Math.min(t + distance, h-ch);
9519                          this.scrollTo("top", v, a);
9520                          scrolled = true;
9521                      }
9522                      break;
9523              }
9524              return scrolled;
9525         },
9526
9527         /**
9528          * Translates the passed page coordinates into left/top css values for this element
9529          * @param {Number/Array} x The page x or an array containing [x, y]
9530          * @param {Number} y The page y
9531          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9532          */
9533         translatePoints : function(x, y){
9534             if(typeof x == 'object' || x instanceof Array){
9535                 y = x[1]; x = x[0];
9536             }
9537             var p = this.getStyle('position');
9538             var o = this.getXY();
9539
9540             var l = parseInt(this.getStyle('left'), 10);
9541             var t = parseInt(this.getStyle('top'), 10);
9542
9543             if(isNaN(l)){
9544                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9545             }
9546             if(isNaN(t)){
9547                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9548             }
9549
9550             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9551         },
9552
9553         /**
9554          * Returns the current scroll position of the element.
9555          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9556          */
9557         getScroll : function(){
9558             var d = this.dom, doc = document;
9559             if(d == doc || d == doc.body){
9560                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9561                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9562                 return {left: l, top: t};
9563             }else{
9564                 return {left: d.scrollLeft, top: d.scrollTop};
9565             }
9566         },
9567
9568         /**
9569          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9570          * are convert to standard 6 digit hex color.
9571          * @param {String} attr The css attribute
9572          * @param {String} defaultValue The default value to use when a valid color isn't found
9573          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9574          * YUI color anims.
9575          */
9576         getColor : function(attr, defaultValue, prefix){
9577             var v = this.getStyle(attr);
9578             if(!v || v == "transparent" || v == "inherit") {
9579                 return defaultValue;
9580             }
9581             var color = typeof prefix == "undefined" ? "#" : prefix;
9582             if(v.substr(0, 4) == "rgb("){
9583                 var rvs = v.slice(4, v.length -1).split(",");
9584                 for(var i = 0; i < 3; i++){
9585                     var h = parseInt(rvs[i]).toString(16);
9586                     if(h < 16){
9587                         h = "0" + h;
9588                     }
9589                     color += h;
9590                 }
9591             } else {
9592                 if(v.substr(0, 1) == "#"){
9593                     if(v.length == 4) {
9594                         for(var i = 1; i < 4; i++){
9595                             var c = v.charAt(i);
9596                             color +=  c + c;
9597                         }
9598                     }else if(v.length == 7){
9599                         color += v.substr(1);
9600                     }
9601                 }
9602             }
9603             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9604         },
9605
9606         /**
9607          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9608          * gradient background, rounded corners and a 4-way shadow.
9609          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9610          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9611          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9612          * @return {Roo.Element} this
9613          */
9614         boxWrap : function(cls){
9615             cls = cls || 'x-box';
9616             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9617             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9618             return el;
9619         },
9620
9621         /**
9622          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9623          * @param {String} namespace The namespace in which to look for the attribute
9624          * @param {String} name The attribute name
9625          * @return {String} The attribute value
9626          */
9627         getAttributeNS : Roo.isIE ? function(ns, name){
9628             var d = this.dom;
9629             var type = typeof d[ns+":"+name];
9630             if(type != 'undefined' && type != 'unknown'){
9631                 return d[ns+":"+name];
9632             }
9633             return d[name];
9634         } : function(ns, name){
9635             var d = this.dom;
9636             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9637         },
9638         
9639         
9640         /**
9641          * Sets or Returns the value the dom attribute value
9642          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9643          * @param {String} value (optional) The value to set the attribute to
9644          * @return {String} The attribute value
9645          */
9646         attr : function(name){
9647             if (arguments.length > 1) {
9648                 this.dom.setAttribute(name, arguments[1]);
9649                 return arguments[1];
9650             }
9651             if (typeof(name) == 'object') {
9652                 for(var i in name) {
9653                     this.attr(i, name[i]);
9654                 }
9655                 return name;
9656             }
9657             
9658             
9659             if (!this.dom.hasAttribute(name)) {
9660                 return undefined;
9661             }
9662             return this.dom.getAttribute(name);
9663         }
9664         
9665         
9666         
9667     };
9668
9669     var ep = El.prototype;
9670
9671     /**
9672      * Appends an event handler (Shorthand for addListener)
9673      * @param {String}   eventName     The type of event to append
9674      * @param {Function} fn        The method the event invokes
9675      * @param {Object} scope       (optional) The scope (this object) of the fn
9676      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9677      * @method
9678      */
9679     ep.on = ep.addListener;
9680         // backwards compat
9681     ep.mon = ep.addListener;
9682
9683     /**
9684      * Removes an event handler from this element (shorthand for removeListener)
9685      * @param {String} eventName the type of event to remove
9686      * @param {Function} fn the method the event invokes
9687      * @return {Roo.Element} this
9688      * @method
9689      */
9690     ep.un = ep.removeListener;
9691
9692     /**
9693      * true to automatically adjust width and height settings for box-model issues (default to true)
9694      */
9695     ep.autoBoxAdjust = true;
9696
9697     // private
9698     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9699
9700     // private
9701     El.addUnits = function(v, defaultUnit){
9702         if(v === "" || v == "auto"){
9703             return v;
9704         }
9705         if(v === undefined){
9706             return '';
9707         }
9708         if(typeof v == "number" || !El.unitPattern.test(v)){
9709             return v + (defaultUnit || 'px');
9710         }
9711         return v;
9712     };
9713
9714     // special markup used throughout Roo when box wrapping elements
9715     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>';
9716     /**
9717      * Visibility mode constant - Use visibility to hide element
9718      * @static
9719      * @type Number
9720      */
9721     El.VISIBILITY = 1;
9722     /**
9723      * Visibility mode constant - Use display to hide element
9724      * @static
9725      * @type Number
9726      */
9727     El.DISPLAY = 2;
9728
9729     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9730     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9731     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9732
9733
9734
9735     /**
9736      * @private
9737      */
9738     El.cache = {};
9739
9740     var docEl;
9741
9742     /**
9743      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9744      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9745      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9746      * @return {Element} The Element object
9747      * @static
9748      */
9749     El.get = function(el){
9750         var ex, elm, id;
9751         if(!el){ return null; }
9752         if(typeof el == "string"){ // element id
9753             if(!(elm = document.getElementById(el))){
9754                 return null;
9755             }
9756             if(ex = El.cache[el]){
9757                 ex.dom = elm;
9758             }else{
9759                 ex = El.cache[el] = new El(elm);
9760             }
9761             return ex;
9762         }else if(el.tagName){ // dom element
9763             if(!(id = el.id)){
9764                 id = Roo.id(el);
9765             }
9766             if(ex = El.cache[id]){
9767                 ex.dom = el;
9768             }else{
9769                 ex = El.cache[id] = new El(el);
9770             }
9771             return ex;
9772         }else if(el instanceof El){
9773             if(el != docEl){
9774                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9775                                                               // catch case where it hasn't been appended
9776                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9777             }
9778             return el;
9779         }else if(el.isComposite){
9780             return el;
9781         }else if(el instanceof Array){
9782             return El.select(el);
9783         }else if(el == document){
9784             // create a bogus element object representing the document object
9785             if(!docEl){
9786                 var f = function(){};
9787                 f.prototype = El.prototype;
9788                 docEl = new f();
9789                 docEl.dom = document;
9790             }
9791             return docEl;
9792         }
9793         return null;
9794     };
9795
9796     // private
9797     El.uncache = function(el){
9798         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9799             if(a[i]){
9800                 delete El.cache[a[i].id || a[i]];
9801             }
9802         }
9803     };
9804
9805     // private
9806     // Garbage collection - uncache elements/purge listeners on orphaned elements
9807     // so we don't hold a reference and cause the browser to retain them
9808     El.garbageCollect = function(){
9809         if(!Roo.enableGarbageCollector){
9810             clearInterval(El.collectorThread);
9811             return;
9812         }
9813         for(var eid in El.cache){
9814             var el = El.cache[eid], d = el.dom;
9815             // -------------------------------------------------------
9816             // Determining what is garbage:
9817             // -------------------------------------------------------
9818             // !d
9819             // dom node is null, definitely garbage
9820             // -------------------------------------------------------
9821             // !d.parentNode
9822             // no parentNode == direct orphan, definitely garbage
9823             // -------------------------------------------------------
9824             // !d.offsetParent && !document.getElementById(eid)
9825             // display none elements have no offsetParent so we will
9826             // also try to look it up by it's id. However, check
9827             // offsetParent first so we don't do unneeded lookups.
9828             // This enables collection of elements that are not orphans
9829             // directly, but somewhere up the line they have an orphan
9830             // parent.
9831             // -------------------------------------------------------
9832             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9833                 delete El.cache[eid];
9834                 if(d && Roo.enableListenerCollection){
9835                     E.purgeElement(d);
9836                 }
9837             }
9838         }
9839     }
9840     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9841
9842
9843     // dom is optional
9844     El.Flyweight = function(dom){
9845         this.dom = dom;
9846     };
9847     El.Flyweight.prototype = El.prototype;
9848
9849     El._flyweights = {};
9850     /**
9851      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9852      * the dom node can be overwritten by other code.
9853      * @param {String/HTMLElement} el The dom node or id
9854      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9855      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9856      * @static
9857      * @return {Element} The shared Element object
9858      */
9859     El.fly = function(el, named){
9860         named = named || '_global';
9861         el = Roo.getDom(el);
9862         if(!el){
9863             return null;
9864         }
9865         if(!El._flyweights[named]){
9866             El._flyweights[named] = new El.Flyweight();
9867         }
9868         El._flyweights[named].dom = el;
9869         return El._flyweights[named];
9870     };
9871
9872     /**
9873      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9874      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9875      * Shorthand of {@link Roo.Element#get}
9876      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9877      * @return {Element} The Element object
9878      * @member Roo
9879      * @method get
9880      */
9881     Roo.get = El.get;
9882     /**
9883      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9884      * the dom node can be overwritten by other code.
9885      * Shorthand of {@link Roo.Element#fly}
9886      * @param {String/HTMLElement} el The dom node or id
9887      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9888      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9889      * @static
9890      * @return {Element} The shared Element object
9891      * @member Roo
9892      * @method fly
9893      */
9894     Roo.fly = El.fly;
9895
9896     // speedy lookup for elements never to box adjust
9897     var noBoxAdjust = Roo.isStrict ? {
9898         select:1
9899     } : {
9900         input:1, select:1, textarea:1
9901     };
9902     if(Roo.isIE || Roo.isGecko){
9903         noBoxAdjust['button'] = 1;
9904     }
9905
9906
9907     Roo.EventManager.on(window, 'unload', function(){
9908         delete El.cache;
9909         delete El._flyweights;
9910     });
9911 })();
9912
9913
9914
9915
9916 if(Roo.DomQuery){
9917     Roo.Element.selectorFunction = Roo.DomQuery.select;
9918 }
9919
9920 Roo.Element.select = function(selector, unique, root){
9921     var els;
9922     if(typeof selector == "string"){
9923         els = Roo.Element.selectorFunction(selector, root);
9924     }else if(selector.length !== undefined){
9925         els = selector;
9926     }else{
9927         throw "Invalid selector";
9928     }
9929     if(unique === true){
9930         return new Roo.CompositeElement(els);
9931     }else{
9932         return new Roo.CompositeElementLite(els);
9933     }
9934 };
9935 /**
9936  * Selects elements based on the passed CSS selector to enable working on them as 1.
9937  * @param {String/Array} selector The CSS selector or an array of elements
9938  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9939  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9940  * @return {CompositeElementLite/CompositeElement}
9941  * @member Roo
9942  * @method select
9943  */
9944 Roo.select = Roo.Element.select;
9945
9946
9947
9948
9949
9950
9951
9952
9953
9954
9955
9956
9957
9958
9959 /*
9960  * Based on:
9961  * Ext JS Library 1.1.1
9962  * Copyright(c) 2006-2007, Ext JS, LLC.
9963  *
9964  * Originally Released Under LGPL - original licence link has changed is not relivant.
9965  *
9966  * Fork - LGPL
9967  * <script type="text/javascript">
9968  */
9969
9970
9971
9972 //Notifies Element that fx methods are available
9973 Roo.enableFx = true;
9974
9975 /**
9976  * @class Roo.Fx
9977  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9978  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9979  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9980  * Element effects to work.</p><br/>
9981  *
9982  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9983  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9984  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9985  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9986  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9987  * expected results and should be done with care.</p><br/>
9988  *
9989  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9990  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9991 <pre>
9992 Value  Description
9993 -----  -----------------------------
9994 tl     The top left corner
9995 t      The center of the top edge
9996 tr     The top right corner
9997 l      The center of the left edge
9998 r      The center of the right edge
9999 bl     The bottom left corner
10000 b      The center of the bottom edge
10001 br     The bottom right corner
10002 </pre>
10003  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10004  * below are common options that can be passed to any Fx method.</b>
10005  * @cfg {Function} callback A function called when the effect is finished
10006  * @cfg {Object} scope The scope of the effect function
10007  * @cfg {String} easing A valid Easing value for the effect
10008  * @cfg {String} afterCls A css class to apply after the effect
10009  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10010  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10011  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10012  * effects that end with the element being visually hidden, ignored otherwise)
10013  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10014  * a function which returns such a specification that will be applied to the Element after the effect finishes
10015  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10016  * @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
10017  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10018  */
10019 Roo.Fx = {
10020         /**
10021          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10022          * origin for the slide effect.  This function automatically handles wrapping the element with
10023          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10024          * Usage:
10025          *<pre><code>
10026 // default: slide the element in from the top
10027 el.slideIn();
10028
10029 // custom: slide the element in from the right with a 2-second duration
10030 el.slideIn('r', { duration: 2 });
10031
10032 // common config options shown with default values
10033 el.slideIn('t', {
10034     easing: 'easeOut',
10035     duration: .5
10036 });
10037 </code></pre>
10038          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10039          * @param {Object} options (optional) Object literal with any of the Fx config options
10040          * @return {Roo.Element} The Element
10041          */
10042     slideIn : function(anchor, o){
10043         var el = this.getFxEl();
10044         o = o || {};
10045
10046         el.queueFx(o, function(){
10047
10048             anchor = anchor || "t";
10049
10050             // fix display to visibility
10051             this.fixDisplay();
10052
10053             // restore values after effect
10054             var r = this.getFxRestore();
10055             var b = this.getBox();
10056             // fixed size for slide
10057             this.setSize(b);
10058
10059             // wrap if needed
10060             var wrap = this.fxWrap(r.pos, o, "hidden");
10061
10062             var st = this.dom.style;
10063             st.visibility = "visible";
10064             st.position = "absolute";
10065
10066             // clear out temp styles after slide and unwrap
10067             var after = function(){
10068                 el.fxUnwrap(wrap, r.pos, o);
10069                 st.width = r.width;
10070                 st.height = r.height;
10071                 el.afterFx(o);
10072             };
10073             // time to calc the positions
10074             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10075
10076             switch(anchor.toLowerCase()){
10077                 case "t":
10078                     wrap.setSize(b.width, 0);
10079                     st.left = st.bottom = "0";
10080                     a = {height: bh};
10081                 break;
10082                 case "l":
10083                     wrap.setSize(0, b.height);
10084                     st.right = st.top = "0";
10085                     a = {width: bw};
10086                 break;
10087                 case "r":
10088                     wrap.setSize(0, b.height);
10089                     wrap.setX(b.right);
10090                     st.left = st.top = "0";
10091                     a = {width: bw, points: pt};
10092                 break;
10093                 case "b":
10094                     wrap.setSize(b.width, 0);
10095                     wrap.setY(b.bottom);
10096                     st.left = st.top = "0";
10097                     a = {height: bh, points: pt};
10098                 break;
10099                 case "tl":
10100                     wrap.setSize(0, 0);
10101                     st.right = st.bottom = "0";
10102                     a = {width: bw, height: bh};
10103                 break;
10104                 case "bl":
10105                     wrap.setSize(0, 0);
10106                     wrap.setY(b.y+b.height);
10107                     st.right = st.top = "0";
10108                     a = {width: bw, height: bh, points: pt};
10109                 break;
10110                 case "br":
10111                     wrap.setSize(0, 0);
10112                     wrap.setXY([b.right, b.bottom]);
10113                     st.left = st.top = "0";
10114                     a = {width: bw, height: bh, points: pt};
10115                 break;
10116                 case "tr":
10117                     wrap.setSize(0, 0);
10118                     wrap.setX(b.x+b.width);
10119                     st.left = st.bottom = "0";
10120                     a = {width: bw, height: bh, points: pt};
10121                 break;
10122             }
10123             this.dom.style.visibility = "visible";
10124             wrap.show();
10125
10126             arguments.callee.anim = wrap.fxanim(a,
10127                 o,
10128                 'motion',
10129                 .5,
10130                 'easeOut', after);
10131         });
10132         return this;
10133     },
10134     
10135         /**
10136          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10137          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10138          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10139          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10140          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10141          * Usage:
10142          *<pre><code>
10143 // default: slide the element out to the top
10144 el.slideOut();
10145
10146 // custom: slide the element out to the right with a 2-second duration
10147 el.slideOut('r', { duration: 2 });
10148
10149 // common config options shown with default values
10150 el.slideOut('t', {
10151     easing: 'easeOut',
10152     duration: .5,
10153     remove: false,
10154     useDisplay: false
10155 });
10156 </code></pre>
10157          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10158          * @param {Object} options (optional) Object literal with any of the Fx config options
10159          * @return {Roo.Element} The Element
10160          */
10161     slideOut : function(anchor, o){
10162         var el = this.getFxEl();
10163         o = o || {};
10164
10165         el.queueFx(o, function(){
10166
10167             anchor = anchor || "t";
10168
10169             // restore values after effect
10170             var r = this.getFxRestore();
10171             
10172             var b = this.getBox();
10173             // fixed size for slide
10174             this.setSize(b);
10175
10176             // wrap if needed
10177             var wrap = this.fxWrap(r.pos, o, "visible");
10178
10179             var st = this.dom.style;
10180             st.visibility = "visible";
10181             st.position = "absolute";
10182
10183             wrap.setSize(b);
10184
10185             var after = function(){
10186                 if(o.useDisplay){
10187                     el.setDisplayed(false);
10188                 }else{
10189                     el.hide();
10190                 }
10191
10192                 el.fxUnwrap(wrap, r.pos, o);
10193
10194                 st.width = r.width;
10195                 st.height = r.height;
10196
10197                 el.afterFx(o);
10198             };
10199
10200             var a, zero = {to: 0};
10201             switch(anchor.toLowerCase()){
10202                 case "t":
10203                     st.left = st.bottom = "0";
10204                     a = {height: zero};
10205                 break;
10206                 case "l":
10207                     st.right = st.top = "0";
10208                     a = {width: zero};
10209                 break;
10210                 case "r":
10211                     st.left = st.top = "0";
10212                     a = {width: zero, points: {to:[b.right, b.y]}};
10213                 break;
10214                 case "b":
10215                     st.left = st.top = "0";
10216                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10217                 break;
10218                 case "tl":
10219                     st.right = st.bottom = "0";
10220                     a = {width: zero, height: zero};
10221                 break;
10222                 case "bl":
10223                     st.right = st.top = "0";
10224                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10225                 break;
10226                 case "br":
10227                     st.left = st.top = "0";
10228                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10229                 break;
10230                 case "tr":
10231                     st.left = st.bottom = "0";
10232                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10233                 break;
10234             }
10235
10236             arguments.callee.anim = wrap.fxanim(a,
10237                 o,
10238                 'motion',
10239                 .5,
10240                 "easeOut", after);
10241         });
10242         return this;
10243     },
10244
10245         /**
10246          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10247          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10248          * The element must be removed from the DOM using the 'remove' config option if desired.
10249          * Usage:
10250          *<pre><code>
10251 // default
10252 el.puff();
10253
10254 // common config options shown with default values
10255 el.puff({
10256     easing: 'easeOut',
10257     duration: .5,
10258     remove: false,
10259     useDisplay: false
10260 });
10261 </code></pre>
10262          * @param {Object} options (optional) Object literal with any of the Fx config options
10263          * @return {Roo.Element} The Element
10264          */
10265     puff : function(o){
10266         var el = this.getFxEl();
10267         o = o || {};
10268
10269         el.queueFx(o, function(){
10270             this.clearOpacity();
10271             this.show();
10272
10273             // restore values after effect
10274             var r = this.getFxRestore();
10275             var st = this.dom.style;
10276
10277             var after = function(){
10278                 if(o.useDisplay){
10279                     el.setDisplayed(false);
10280                 }else{
10281                     el.hide();
10282                 }
10283
10284                 el.clearOpacity();
10285
10286                 el.setPositioning(r.pos);
10287                 st.width = r.width;
10288                 st.height = r.height;
10289                 st.fontSize = '';
10290                 el.afterFx(o);
10291             };
10292
10293             var width = this.getWidth();
10294             var height = this.getHeight();
10295
10296             arguments.callee.anim = this.fxanim({
10297                     width : {to: this.adjustWidth(width * 2)},
10298                     height : {to: this.adjustHeight(height * 2)},
10299                     points : {by: [-(width * .5), -(height * .5)]},
10300                     opacity : {to: 0},
10301                     fontSize: {to:200, unit: "%"}
10302                 },
10303                 o,
10304                 'motion',
10305                 .5,
10306                 "easeOut", after);
10307         });
10308         return this;
10309     },
10310
10311         /**
10312          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10313          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10314          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10315          * Usage:
10316          *<pre><code>
10317 // default
10318 el.switchOff();
10319
10320 // all config options shown with default values
10321 el.switchOff({
10322     easing: 'easeIn',
10323     duration: .3,
10324     remove: false,
10325     useDisplay: false
10326 });
10327 </code></pre>
10328          * @param {Object} options (optional) Object literal with any of the Fx config options
10329          * @return {Roo.Element} The Element
10330          */
10331     switchOff : function(o){
10332         var el = this.getFxEl();
10333         o = o || {};
10334
10335         el.queueFx(o, function(){
10336             this.clearOpacity();
10337             this.clip();
10338
10339             // restore values after effect
10340             var r = this.getFxRestore();
10341             var st = this.dom.style;
10342
10343             var after = function(){
10344                 if(o.useDisplay){
10345                     el.setDisplayed(false);
10346                 }else{
10347                     el.hide();
10348                 }
10349
10350                 el.clearOpacity();
10351                 el.setPositioning(r.pos);
10352                 st.width = r.width;
10353                 st.height = r.height;
10354
10355                 el.afterFx(o);
10356             };
10357
10358             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10359                 this.clearOpacity();
10360                 (function(){
10361                     this.fxanim({
10362                         height:{to:1},
10363                         points:{by:[0, this.getHeight() * .5]}
10364                     }, o, 'motion', 0.3, 'easeIn', after);
10365                 }).defer(100, this);
10366             });
10367         });
10368         return this;
10369     },
10370
10371     /**
10372      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10373      * changed using the "attr" config option) and then fading back to the original color. If no original
10374      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10375      * Usage:
10376 <pre><code>
10377 // default: highlight background to yellow
10378 el.highlight();
10379
10380 // custom: highlight foreground text to blue for 2 seconds
10381 el.highlight("0000ff", { attr: 'color', duration: 2 });
10382
10383 // common config options shown with default values
10384 el.highlight("ffff9c", {
10385     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10386     endColor: (current color) or "ffffff",
10387     easing: 'easeIn',
10388     duration: 1
10389 });
10390 </code></pre>
10391      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10392      * @param {Object} options (optional) Object literal with any of the Fx config options
10393      * @return {Roo.Element} The Element
10394      */ 
10395     highlight : function(color, o){
10396         var el = this.getFxEl();
10397         o = o || {};
10398
10399         el.queueFx(o, function(){
10400             color = color || "ffff9c";
10401             attr = o.attr || "backgroundColor";
10402
10403             this.clearOpacity();
10404             this.show();
10405
10406             var origColor = this.getColor(attr);
10407             var restoreColor = this.dom.style[attr];
10408             endColor = (o.endColor || origColor) || "ffffff";
10409
10410             var after = function(){
10411                 el.dom.style[attr] = restoreColor;
10412                 el.afterFx(o);
10413             };
10414
10415             var a = {};
10416             a[attr] = {from: color, to: endColor};
10417             arguments.callee.anim = this.fxanim(a,
10418                 o,
10419                 'color',
10420                 1,
10421                 'easeIn', after);
10422         });
10423         return this;
10424     },
10425
10426    /**
10427     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10428     * Usage:
10429 <pre><code>
10430 // default: a single light blue ripple
10431 el.frame();
10432
10433 // custom: 3 red ripples lasting 3 seconds total
10434 el.frame("ff0000", 3, { duration: 3 });
10435
10436 // common config options shown with default values
10437 el.frame("C3DAF9", 1, {
10438     duration: 1 //duration of entire animation (not each individual ripple)
10439     // Note: Easing is not configurable and will be ignored if included
10440 });
10441 </code></pre>
10442     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10443     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10444     * @param {Object} options (optional) Object literal with any of the Fx config options
10445     * @return {Roo.Element} The Element
10446     */
10447     frame : function(color, count, o){
10448         var el = this.getFxEl();
10449         o = o || {};
10450
10451         el.queueFx(o, function(){
10452             color = color || "#C3DAF9";
10453             if(color.length == 6){
10454                 color = "#" + color;
10455             }
10456             count = count || 1;
10457             duration = o.duration || 1;
10458             this.show();
10459
10460             var b = this.getBox();
10461             var animFn = function(){
10462                 var proxy = this.createProxy({
10463
10464                      style:{
10465                         visbility:"hidden",
10466                         position:"absolute",
10467                         "z-index":"35000", // yee haw
10468                         border:"0px solid " + color
10469                      }
10470                   });
10471                 var scale = Roo.isBorderBox ? 2 : 1;
10472                 proxy.animate({
10473                     top:{from:b.y, to:b.y - 20},
10474                     left:{from:b.x, to:b.x - 20},
10475                     borderWidth:{from:0, to:10},
10476                     opacity:{from:1, to:0},
10477                     height:{from:b.height, to:(b.height + (20*scale))},
10478                     width:{from:b.width, to:(b.width + (20*scale))}
10479                 }, duration, function(){
10480                     proxy.remove();
10481                 });
10482                 if(--count > 0){
10483                      animFn.defer((duration/2)*1000, this);
10484                 }else{
10485                     el.afterFx(o);
10486                 }
10487             };
10488             animFn.call(this);
10489         });
10490         return this;
10491     },
10492
10493    /**
10494     * Creates a pause before any subsequent queued effects begin.  If there are
10495     * no effects queued after the pause it will have no effect.
10496     * Usage:
10497 <pre><code>
10498 el.pause(1);
10499 </code></pre>
10500     * @param {Number} seconds The length of time to pause (in seconds)
10501     * @return {Roo.Element} The Element
10502     */
10503     pause : function(seconds){
10504         var el = this.getFxEl();
10505         var o = {};
10506
10507         el.queueFx(o, function(){
10508             setTimeout(function(){
10509                 el.afterFx(o);
10510             }, seconds * 1000);
10511         });
10512         return this;
10513     },
10514
10515    /**
10516     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10517     * using the "endOpacity" config option.
10518     * Usage:
10519 <pre><code>
10520 // default: fade in from opacity 0 to 100%
10521 el.fadeIn();
10522
10523 // custom: fade in from opacity 0 to 75% over 2 seconds
10524 el.fadeIn({ endOpacity: .75, duration: 2});
10525
10526 // common config options shown with default values
10527 el.fadeIn({
10528     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10529     easing: 'easeOut',
10530     duration: .5
10531 });
10532 </code></pre>
10533     * @param {Object} options (optional) Object literal with any of the Fx config options
10534     * @return {Roo.Element} The Element
10535     */
10536     fadeIn : function(o){
10537         var el = this.getFxEl();
10538         o = o || {};
10539         el.queueFx(o, function(){
10540             this.setOpacity(0);
10541             this.fixDisplay();
10542             this.dom.style.visibility = 'visible';
10543             var to = o.endOpacity || 1;
10544             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10545                 o, null, .5, "easeOut", function(){
10546                 if(to == 1){
10547                     this.clearOpacity();
10548                 }
10549                 el.afterFx(o);
10550             });
10551         });
10552         return this;
10553     },
10554
10555    /**
10556     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10557     * using the "endOpacity" config option.
10558     * Usage:
10559 <pre><code>
10560 // default: fade out from the element's current opacity to 0
10561 el.fadeOut();
10562
10563 // custom: fade out from the element's current opacity to 25% over 2 seconds
10564 el.fadeOut({ endOpacity: .25, duration: 2});
10565
10566 // common config options shown with default values
10567 el.fadeOut({
10568     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10569     easing: 'easeOut',
10570     duration: .5
10571     remove: false,
10572     useDisplay: false
10573 });
10574 </code></pre>
10575     * @param {Object} options (optional) Object literal with any of the Fx config options
10576     * @return {Roo.Element} The Element
10577     */
10578     fadeOut : function(o){
10579         var el = this.getFxEl();
10580         o = o || {};
10581         el.queueFx(o, function(){
10582             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10583                 o, null, .5, "easeOut", function(){
10584                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10585                      this.dom.style.display = "none";
10586                 }else{
10587                      this.dom.style.visibility = "hidden";
10588                 }
10589                 this.clearOpacity();
10590                 el.afterFx(o);
10591             });
10592         });
10593         return this;
10594     },
10595
10596    /**
10597     * Animates the transition of an element's dimensions from a starting height/width
10598     * to an ending height/width.
10599     * Usage:
10600 <pre><code>
10601 // change height and width to 100x100 pixels
10602 el.scale(100, 100);
10603
10604 // common config options shown with default values.  The height and width will default to
10605 // the element's existing values if passed as null.
10606 el.scale(
10607     [element's width],
10608     [element's height], {
10609     easing: 'easeOut',
10610     duration: .35
10611 });
10612 </code></pre>
10613     * @param {Number} width  The new width (pass undefined to keep the original width)
10614     * @param {Number} height  The new height (pass undefined to keep the original height)
10615     * @param {Object} options (optional) Object literal with any of the Fx config options
10616     * @return {Roo.Element} The Element
10617     */
10618     scale : function(w, h, o){
10619         this.shift(Roo.apply({}, o, {
10620             width: w,
10621             height: h
10622         }));
10623         return this;
10624     },
10625
10626    /**
10627     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10628     * Any of these properties not specified in the config object will not be changed.  This effect 
10629     * requires that at least one new dimension, position or opacity setting must be passed in on
10630     * the config object in order for the function to have any effect.
10631     * Usage:
10632 <pre><code>
10633 // slide the element horizontally to x position 200 while changing the height and opacity
10634 el.shift({ x: 200, height: 50, opacity: .8 });
10635
10636 // common config options shown with default values.
10637 el.shift({
10638     width: [element's width],
10639     height: [element's height],
10640     x: [element's x position],
10641     y: [element's y position],
10642     opacity: [element's opacity],
10643     easing: 'easeOut',
10644     duration: .35
10645 });
10646 </code></pre>
10647     * @param {Object} options  Object literal with any of the Fx config options
10648     * @return {Roo.Element} The Element
10649     */
10650     shift : function(o){
10651         var el = this.getFxEl();
10652         o = o || {};
10653         el.queueFx(o, function(){
10654             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10655             if(w !== undefined){
10656                 a.width = {to: this.adjustWidth(w)};
10657             }
10658             if(h !== undefined){
10659                 a.height = {to: this.adjustHeight(h)};
10660             }
10661             if(x !== undefined || y !== undefined){
10662                 a.points = {to: [
10663                     x !== undefined ? x : this.getX(),
10664                     y !== undefined ? y : this.getY()
10665                 ]};
10666             }
10667             if(op !== undefined){
10668                 a.opacity = {to: op};
10669             }
10670             if(o.xy !== undefined){
10671                 a.points = {to: o.xy};
10672             }
10673             arguments.callee.anim = this.fxanim(a,
10674                 o, 'motion', .35, "easeOut", function(){
10675                 el.afterFx(o);
10676             });
10677         });
10678         return this;
10679     },
10680
10681         /**
10682          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10683          * ending point of the effect.
10684          * Usage:
10685          *<pre><code>
10686 // default: slide the element downward while fading out
10687 el.ghost();
10688
10689 // custom: slide the element out to the right with a 2-second duration
10690 el.ghost('r', { duration: 2 });
10691
10692 // common config options shown with default values
10693 el.ghost('b', {
10694     easing: 'easeOut',
10695     duration: .5
10696     remove: false,
10697     useDisplay: false
10698 });
10699 </code></pre>
10700          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10701          * @param {Object} options (optional) Object literal with any of the Fx config options
10702          * @return {Roo.Element} The Element
10703          */
10704     ghost : function(anchor, o){
10705         var el = this.getFxEl();
10706         o = o || {};
10707
10708         el.queueFx(o, function(){
10709             anchor = anchor || "b";
10710
10711             // restore values after effect
10712             var r = this.getFxRestore();
10713             var w = this.getWidth(),
10714                 h = this.getHeight();
10715
10716             var st = this.dom.style;
10717
10718             var after = function(){
10719                 if(o.useDisplay){
10720                     el.setDisplayed(false);
10721                 }else{
10722                     el.hide();
10723                 }
10724
10725                 el.clearOpacity();
10726                 el.setPositioning(r.pos);
10727                 st.width = r.width;
10728                 st.height = r.height;
10729
10730                 el.afterFx(o);
10731             };
10732
10733             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10734             switch(anchor.toLowerCase()){
10735                 case "t":
10736                     pt.by = [0, -h];
10737                 break;
10738                 case "l":
10739                     pt.by = [-w, 0];
10740                 break;
10741                 case "r":
10742                     pt.by = [w, 0];
10743                 break;
10744                 case "b":
10745                     pt.by = [0, h];
10746                 break;
10747                 case "tl":
10748                     pt.by = [-w, -h];
10749                 break;
10750                 case "bl":
10751                     pt.by = [-w, h];
10752                 break;
10753                 case "br":
10754                     pt.by = [w, h];
10755                 break;
10756                 case "tr":
10757                     pt.by = [w, -h];
10758                 break;
10759             }
10760
10761             arguments.callee.anim = this.fxanim(a,
10762                 o,
10763                 'motion',
10764                 .5,
10765                 "easeOut", after);
10766         });
10767         return this;
10768     },
10769
10770         /**
10771          * Ensures that all effects queued after syncFx is called on the element are
10772          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10773          * @return {Roo.Element} The Element
10774          */
10775     syncFx : function(){
10776         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10777             block : false,
10778             concurrent : true,
10779             stopFx : false
10780         });
10781         return this;
10782     },
10783
10784         /**
10785          * Ensures that all effects queued after sequenceFx is called on the element are
10786          * run in sequence.  This is the opposite of {@link #syncFx}.
10787          * @return {Roo.Element} The Element
10788          */
10789     sequenceFx : function(){
10790         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10791             block : false,
10792             concurrent : false,
10793             stopFx : false
10794         });
10795         return this;
10796     },
10797
10798         /* @private */
10799     nextFx : function(){
10800         var ef = this.fxQueue[0];
10801         if(ef){
10802             ef.call(this);
10803         }
10804     },
10805
10806         /**
10807          * Returns true if the element has any effects actively running or queued, else returns false.
10808          * @return {Boolean} True if element has active effects, else false
10809          */
10810     hasActiveFx : function(){
10811         return this.fxQueue && this.fxQueue[0];
10812     },
10813
10814         /**
10815          * Stops any running effects and clears the element's internal effects queue if it contains
10816          * any additional effects that haven't started yet.
10817          * @return {Roo.Element} The Element
10818          */
10819     stopFx : function(){
10820         if(this.hasActiveFx()){
10821             var cur = this.fxQueue[0];
10822             if(cur && cur.anim && cur.anim.isAnimated()){
10823                 this.fxQueue = [cur]; // clear out others
10824                 cur.anim.stop(true);
10825             }
10826         }
10827         return this;
10828     },
10829
10830         /* @private */
10831     beforeFx : function(o){
10832         if(this.hasActiveFx() && !o.concurrent){
10833            if(o.stopFx){
10834                this.stopFx();
10835                return true;
10836            }
10837            return false;
10838         }
10839         return true;
10840     },
10841
10842         /**
10843          * Returns true if the element is currently blocking so that no other effect can be queued
10844          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10845          * used to ensure that an effect initiated by a user action runs to completion prior to the
10846          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10847          * @return {Boolean} True if blocking, else false
10848          */
10849     hasFxBlock : function(){
10850         var q = this.fxQueue;
10851         return q && q[0] && q[0].block;
10852     },
10853
10854         /* @private */
10855     queueFx : function(o, fn){
10856         if(!this.fxQueue){
10857             this.fxQueue = [];
10858         }
10859         if(!this.hasFxBlock()){
10860             Roo.applyIf(o, this.fxDefaults);
10861             if(!o.concurrent){
10862                 var run = this.beforeFx(o);
10863                 fn.block = o.block;
10864                 this.fxQueue.push(fn);
10865                 if(run){
10866                     this.nextFx();
10867                 }
10868             }else{
10869                 fn.call(this);
10870             }
10871         }
10872         return this;
10873     },
10874
10875         /* @private */
10876     fxWrap : function(pos, o, vis){
10877         var wrap;
10878         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10879             var wrapXY;
10880             if(o.fixPosition){
10881                 wrapXY = this.getXY();
10882             }
10883             var div = document.createElement("div");
10884             div.style.visibility = vis;
10885             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10886             wrap.setPositioning(pos);
10887             if(wrap.getStyle("position") == "static"){
10888                 wrap.position("relative");
10889             }
10890             this.clearPositioning('auto');
10891             wrap.clip();
10892             wrap.dom.appendChild(this.dom);
10893             if(wrapXY){
10894                 wrap.setXY(wrapXY);
10895             }
10896         }
10897         return wrap;
10898     },
10899
10900         /* @private */
10901     fxUnwrap : function(wrap, pos, o){
10902         this.clearPositioning();
10903         this.setPositioning(pos);
10904         if(!o.wrap){
10905             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10906             wrap.remove();
10907         }
10908     },
10909
10910         /* @private */
10911     getFxRestore : function(){
10912         var st = this.dom.style;
10913         return {pos: this.getPositioning(), width: st.width, height : st.height};
10914     },
10915
10916         /* @private */
10917     afterFx : function(o){
10918         if(o.afterStyle){
10919             this.applyStyles(o.afterStyle);
10920         }
10921         if(o.afterCls){
10922             this.addClass(o.afterCls);
10923         }
10924         if(o.remove === true){
10925             this.remove();
10926         }
10927         Roo.callback(o.callback, o.scope, [this]);
10928         if(!o.concurrent){
10929             this.fxQueue.shift();
10930             this.nextFx();
10931         }
10932     },
10933
10934         /* @private */
10935     getFxEl : function(){ // support for composite element fx
10936         return Roo.get(this.dom);
10937     },
10938
10939         /* @private */
10940     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10941         animType = animType || 'run';
10942         opt = opt || {};
10943         var anim = Roo.lib.Anim[animType](
10944             this.dom, args,
10945             (opt.duration || defaultDur) || .35,
10946             (opt.easing || defaultEase) || 'easeOut',
10947             function(){
10948                 Roo.callback(cb, this);
10949             },
10950             this
10951         );
10952         opt.anim = anim;
10953         return anim;
10954     }
10955 };
10956
10957 // backwords compat
10958 Roo.Fx.resize = Roo.Fx.scale;
10959
10960 //When included, Roo.Fx is automatically applied to Element so that all basic
10961 //effects are available directly via the Element API
10962 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10963  * Based on:
10964  * Ext JS Library 1.1.1
10965  * Copyright(c) 2006-2007, Ext JS, LLC.
10966  *
10967  * Originally Released Under LGPL - original licence link has changed is not relivant.
10968  *
10969  * Fork - LGPL
10970  * <script type="text/javascript">
10971  */
10972
10973
10974 /**
10975  * @class Roo.CompositeElement
10976  * Standard composite class. Creates a Roo.Element for every element in the collection.
10977  * <br><br>
10978  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10979  * actions will be performed on all the elements in this collection.</b>
10980  * <br><br>
10981  * All methods return <i>this</i> and can be chained.
10982  <pre><code>
10983  var els = Roo.select("#some-el div.some-class", true);
10984  // or select directly from an existing element
10985  var el = Roo.get('some-el');
10986  el.select('div.some-class', true);
10987
10988  els.setWidth(100); // all elements become 100 width
10989  els.hide(true); // all elements fade out and hide
10990  // or
10991  els.setWidth(100).hide(true);
10992  </code></pre>
10993  */
10994 Roo.CompositeElement = function(els){
10995     this.elements = [];
10996     this.addElements(els);
10997 };
10998 Roo.CompositeElement.prototype = {
10999     isComposite: true,
11000     addElements : function(els){
11001         if(!els) return this;
11002         if(typeof els == "string"){
11003             els = Roo.Element.selectorFunction(els);
11004         }
11005         var yels = this.elements;
11006         var index = yels.length-1;
11007         for(var i = 0, len = els.length; i < len; i++) {
11008                 yels[++index] = Roo.get(els[i]);
11009         }
11010         return this;
11011     },
11012
11013     /**
11014     * Clears this composite and adds the elements returned by the passed selector.
11015     * @param {String/Array} els A string CSS selector, an array of elements or an element
11016     * @return {CompositeElement} this
11017     */
11018     fill : function(els){
11019         this.elements = [];
11020         this.add(els);
11021         return this;
11022     },
11023
11024     /**
11025     * Filters this composite to only elements that match the passed selector.
11026     * @param {String} selector A string CSS selector
11027     * @param {Boolean} inverse return inverse filter (not matches)
11028     * @return {CompositeElement} this
11029     */
11030     filter : function(selector, inverse){
11031         var els = [];
11032         inverse = inverse || false;
11033         this.each(function(el){
11034             var match = inverse ? !el.is(selector) : el.is(selector);
11035             if(match){
11036                 els[els.length] = el.dom;
11037             }
11038         });
11039         this.fill(els);
11040         return this;
11041     },
11042
11043     invoke : function(fn, args){
11044         var els = this.elements;
11045         for(var i = 0, len = els.length; i < len; i++) {
11046                 Roo.Element.prototype[fn].apply(els[i], args);
11047         }
11048         return this;
11049     },
11050     /**
11051     * Adds elements to this composite.
11052     * @param {String/Array} els A string CSS selector, an array of elements or an element
11053     * @return {CompositeElement} this
11054     */
11055     add : function(els){
11056         if(typeof els == "string"){
11057             this.addElements(Roo.Element.selectorFunction(els));
11058         }else if(els.length !== undefined){
11059             this.addElements(els);
11060         }else{
11061             this.addElements([els]);
11062         }
11063         return this;
11064     },
11065     /**
11066     * Calls the passed function passing (el, this, index) for each element in this composite.
11067     * @param {Function} fn The function to call
11068     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11069     * @return {CompositeElement} this
11070     */
11071     each : function(fn, scope){
11072         var els = this.elements;
11073         for(var i = 0, len = els.length; i < len; i++){
11074             if(fn.call(scope || els[i], els[i], this, i) === false) {
11075                 break;
11076             }
11077         }
11078         return this;
11079     },
11080
11081     /**
11082      * Returns the Element object at the specified index
11083      * @param {Number} index
11084      * @return {Roo.Element}
11085      */
11086     item : function(index){
11087         return this.elements[index] || null;
11088     },
11089
11090     /**
11091      * Returns the first Element
11092      * @return {Roo.Element}
11093      */
11094     first : function(){
11095         return this.item(0);
11096     },
11097
11098     /**
11099      * Returns the last Element
11100      * @return {Roo.Element}
11101      */
11102     last : function(){
11103         return this.item(this.elements.length-1);
11104     },
11105
11106     /**
11107      * Returns the number of elements in this composite
11108      * @return Number
11109      */
11110     getCount : function(){
11111         return this.elements.length;
11112     },
11113
11114     /**
11115      * Returns true if this composite contains the passed element
11116      * @return Boolean
11117      */
11118     contains : function(el){
11119         return this.indexOf(el) !== -1;
11120     },
11121
11122     /**
11123      * Returns true if this composite contains the passed element
11124      * @return Boolean
11125      */
11126     indexOf : function(el){
11127         return this.elements.indexOf(Roo.get(el));
11128     },
11129
11130
11131     /**
11132     * Removes the specified element(s).
11133     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11134     * or an array of any of those.
11135     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11136     * @return {CompositeElement} this
11137     */
11138     removeElement : function(el, removeDom){
11139         if(el instanceof Array){
11140             for(var i = 0, len = el.length; i < len; i++){
11141                 this.removeElement(el[i]);
11142             }
11143             return this;
11144         }
11145         var index = typeof el == 'number' ? el : this.indexOf(el);
11146         if(index !== -1){
11147             if(removeDom){
11148                 var d = this.elements[index];
11149                 if(d.dom){
11150                     d.remove();
11151                 }else{
11152                     d.parentNode.removeChild(d);
11153                 }
11154             }
11155             this.elements.splice(index, 1);
11156         }
11157         return this;
11158     },
11159
11160     /**
11161     * Replaces the specified element with the passed element.
11162     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11163     * to replace.
11164     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11165     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11166     * @return {CompositeElement} this
11167     */
11168     replaceElement : function(el, replacement, domReplace){
11169         var index = typeof el == 'number' ? el : this.indexOf(el);
11170         if(index !== -1){
11171             if(domReplace){
11172                 this.elements[index].replaceWith(replacement);
11173             }else{
11174                 this.elements.splice(index, 1, Roo.get(replacement))
11175             }
11176         }
11177         return this;
11178     },
11179
11180     /**
11181      * Removes all elements.
11182      */
11183     clear : function(){
11184         this.elements = [];
11185     }
11186 };
11187 (function(){
11188     Roo.CompositeElement.createCall = function(proto, fnName){
11189         if(!proto[fnName]){
11190             proto[fnName] = function(){
11191                 return this.invoke(fnName, arguments);
11192             };
11193         }
11194     };
11195     for(var fnName in Roo.Element.prototype){
11196         if(typeof Roo.Element.prototype[fnName] == "function"){
11197             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11198         }
11199     };
11200 })();
11201 /*
11202  * Based on:
11203  * Ext JS Library 1.1.1
11204  * Copyright(c) 2006-2007, Ext JS, LLC.
11205  *
11206  * Originally Released Under LGPL - original licence link has changed is not relivant.
11207  *
11208  * Fork - LGPL
11209  * <script type="text/javascript">
11210  */
11211
11212 /**
11213  * @class Roo.CompositeElementLite
11214  * @extends Roo.CompositeElement
11215  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11216  <pre><code>
11217  var els = Roo.select("#some-el div.some-class");
11218  // or select directly from an existing element
11219  var el = Roo.get('some-el');
11220  el.select('div.some-class');
11221
11222  els.setWidth(100); // all elements become 100 width
11223  els.hide(true); // all elements fade out and hide
11224  // or
11225  els.setWidth(100).hide(true);
11226  </code></pre><br><br>
11227  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11228  * actions will be performed on all the elements in this collection.</b>
11229  */
11230 Roo.CompositeElementLite = function(els){
11231     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11232     this.el = new Roo.Element.Flyweight();
11233 };
11234 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11235     addElements : function(els){
11236         if(els){
11237             if(els instanceof Array){
11238                 this.elements = this.elements.concat(els);
11239             }else{
11240                 var yels = this.elements;
11241                 var index = yels.length-1;
11242                 for(var i = 0, len = els.length; i < len; i++) {
11243                     yels[++index] = els[i];
11244                 }
11245             }
11246         }
11247         return this;
11248     },
11249     invoke : function(fn, args){
11250         var els = this.elements;
11251         var el = this.el;
11252         for(var i = 0, len = els.length; i < len; i++) {
11253             el.dom = els[i];
11254                 Roo.Element.prototype[fn].apply(el, args);
11255         }
11256         return this;
11257     },
11258     /**
11259      * Returns a flyweight Element of the dom element object at the specified index
11260      * @param {Number} index
11261      * @return {Roo.Element}
11262      */
11263     item : function(index){
11264         if(!this.elements[index]){
11265             return null;
11266         }
11267         this.el.dom = this.elements[index];
11268         return this.el;
11269     },
11270
11271     // fixes scope with flyweight
11272     addListener : function(eventName, handler, scope, opt){
11273         var els = this.elements;
11274         for(var i = 0, len = els.length; i < len; i++) {
11275             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11276         }
11277         return this;
11278     },
11279
11280     /**
11281     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11282     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11283     * a reference to the dom node, use el.dom.</b>
11284     * @param {Function} fn The function to call
11285     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11286     * @return {CompositeElement} this
11287     */
11288     each : function(fn, scope){
11289         var els = this.elements;
11290         var el = this.el;
11291         for(var i = 0, len = els.length; i < len; i++){
11292             el.dom = els[i];
11293                 if(fn.call(scope || el, el, this, i) === false){
11294                 break;
11295             }
11296         }
11297         return this;
11298     },
11299
11300     indexOf : function(el){
11301         return this.elements.indexOf(Roo.getDom(el));
11302     },
11303
11304     replaceElement : function(el, replacement, domReplace){
11305         var index = typeof el == 'number' ? el : this.indexOf(el);
11306         if(index !== -1){
11307             replacement = Roo.getDom(replacement);
11308             if(domReplace){
11309                 var d = this.elements[index];
11310                 d.parentNode.insertBefore(replacement, d);
11311                 d.parentNode.removeChild(d);
11312             }
11313             this.elements.splice(index, 1, replacement);
11314         }
11315         return this;
11316     }
11317 });
11318 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11319
11320 /*
11321  * Based on:
11322  * Ext JS Library 1.1.1
11323  * Copyright(c) 2006-2007, Ext JS, LLC.
11324  *
11325  * Originally Released Under LGPL - original licence link has changed is not relivant.
11326  *
11327  * Fork - LGPL
11328  * <script type="text/javascript">
11329  */
11330
11331  
11332
11333 /**
11334  * @class Roo.data.Connection
11335  * @extends Roo.util.Observable
11336  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11337  * either to a configured URL, or to a URL specified at request time.<br><br>
11338  * <p>
11339  * Requests made by this class are asynchronous, and will return immediately. No data from
11340  * the server will be available to the statement immediately following the {@link #request} call.
11341  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11342  * <p>
11343  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11344  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11345  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11346  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11347  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11348  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11349  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11350  * standard DOM methods.
11351  * @constructor
11352  * @param {Object} config a configuration object.
11353  */
11354 Roo.data.Connection = function(config){
11355     Roo.apply(this, config);
11356     this.addEvents({
11357         /**
11358          * @event beforerequest
11359          * Fires before a network request is made to retrieve a data object.
11360          * @param {Connection} conn This Connection object.
11361          * @param {Object} options The options config object passed to the {@link #request} method.
11362          */
11363         "beforerequest" : true,
11364         /**
11365          * @event requestcomplete
11366          * Fires if the request was successfully completed.
11367          * @param {Connection} conn This Connection object.
11368          * @param {Object} response The XHR object containing the response data.
11369          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11370          * @param {Object} options The options config object passed to the {@link #request} method.
11371          */
11372         "requestcomplete" : true,
11373         /**
11374          * @event requestexception
11375          * Fires if an error HTTP status was returned from the server.
11376          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11377          * @param {Connection} conn This Connection object.
11378          * @param {Object} response The XHR object containing the response data.
11379          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11380          * @param {Object} options The options config object passed to the {@link #request} method.
11381          */
11382         "requestexception" : true
11383     });
11384     Roo.data.Connection.superclass.constructor.call(this);
11385 };
11386
11387 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11388     /**
11389      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11390      */
11391     /**
11392      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11393      * extra parameters to each request made by this object. (defaults to undefined)
11394      */
11395     /**
11396      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11397      *  to each request made by this object. (defaults to undefined)
11398      */
11399     /**
11400      * @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)
11401      */
11402     /**
11403      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11404      */
11405     timeout : 30000,
11406     /**
11407      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11408      * @type Boolean
11409      */
11410     autoAbort:false,
11411
11412     /**
11413      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11414      * @type Boolean
11415      */
11416     disableCaching: true,
11417
11418     /**
11419      * Sends an HTTP request to a remote server.
11420      * @param {Object} options An object which may contain the following properties:<ul>
11421      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11422      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11423      * request, a url encoded string or a function to call to get either.</li>
11424      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11425      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11426      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11427      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11428      * <li>options {Object} The parameter to the request call.</li>
11429      * <li>success {Boolean} True if the request succeeded.</li>
11430      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11431      * </ul></li>
11432      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11433      * The callback is passed the following parameters:<ul>
11434      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11435      * <li>options {Object} The parameter to the request call.</li>
11436      * </ul></li>
11437      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11438      * The callback is passed the following parameters:<ul>
11439      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11440      * <li>options {Object} The parameter to the request call.</li>
11441      * </ul></li>
11442      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11443      * for the callback function. Defaults to the browser window.</li>
11444      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11445      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11446      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11447      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11448      * params for the post data. Any params will be appended to the URL.</li>
11449      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11450      * </ul>
11451      * @return {Number} transactionId
11452      */
11453     request : function(o){
11454         if(this.fireEvent("beforerequest", this, o) !== false){
11455             var p = o.params;
11456
11457             if(typeof p == "function"){
11458                 p = p.call(o.scope||window, o);
11459             }
11460             if(typeof p == "object"){
11461                 p = Roo.urlEncode(o.params);
11462             }
11463             if(this.extraParams){
11464                 var extras = Roo.urlEncode(this.extraParams);
11465                 p = p ? (p + '&' + extras) : extras;
11466             }
11467
11468             var url = o.url || this.url;
11469             if(typeof url == 'function'){
11470                 url = url.call(o.scope||window, o);
11471             }
11472
11473             if(o.form){
11474                 var form = Roo.getDom(o.form);
11475                 url = url || form.action;
11476
11477                 var enctype = form.getAttribute("enctype");
11478                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11479                     return this.doFormUpload(o, p, url);
11480                 }
11481                 var f = Roo.lib.Ajax.serializeForm(form);
11482                 p = p ? (p + '&' + f) : f;
11483             }
11484
11485             var hs = o.headers;
11486             if(this.defaultHeaders){
11487                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11488                 if(!o.headers){
11489                     o.headers = hs;
11490                 }
11491             }
11492
11493             var cb = {
11494                 success: this.handleResponse,
11495                 failure: this.handleFailure,
11496                 scope: this,
11497                 argument: {options: o},
11498                 timeout : o.timeout || this.timeout
11499             };
11500
11501             var method = o.method||this.method||(p ? "POST" : "GET");
11502
11503             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11504                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11505             }
11506
11507             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11508                 if(o.autoAbort){
11509                     this.abort();
11510                 }
11511             }else if(this.autoAbort !== false){
11512                 this.abort();
11513             }
11514
11515             if((method == 'GET' && p) || o.xmlData){
11516                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11517                 p = '';
11518             }
11519             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11520             return this.transId;
11521         }else{
11522             Roo.callback(o.callback, o.scope, [o, null, null]);
11523             return null;
11524         }
11525     },
11526
11527     /**
11528      * Determine whether this object has a request outstanding.
11529      * @param {Number} transactionId (Optional) defaults to the last transaction
11530      * @return {Boolean} True if there is an outstanding request.
11531      */
11532     isLoading : function(transId){
11533         if(transId){
11534             return Roo.lib.Ajax.isCallInProgress(transId);
11535         }else{
11536             return this.transId ? true : false;
11537         }
11538     },
11539
11540     /**
11541      * Aborts any outstanding request.
11542      * @param {Number} transactionId (Optional) defaults to the last transaction
11543      */
11544     abort : function(transId){
11545         if(transId || this.isLoading()){
11546             Roo.lib.Ajax.abort(transId || this.transId);
11547         }
11548     },
11549
11550     // private
11551     handleResponse : function(response){
11552         this.transId = false;
11553         var options = response.argument.options;
11554         response.argument = options ? options.argument : null;
11555         this.fireEvent("requestcomplete", this, response, options);
11556         Roo.callback(options.success, options.scope, [response, options]);
11557         Roo.callback(options.callback, options.scope, [options, true, response]);
11558     },
11559
11560     // private
11561     handleFailure : function(response, e){
11562         this.transId = false;
11563         var options = response.argument.options;
11564         response.argument = options ? options.argument : null;
11565         this.fireEvent("requestexception", this, response, options, e);
11566         Roo.callback(options.failure, options.scope, [response, options]);
11567         Roo.callback(options.callback, options.scope, [options, false, response]);
11568     },
11569
11570     // private
11571     doFormUpload : function(o, ps, url){
11572         var id = Roo.id();
11573         var frame = document.createElement('iframe');
11574         frame.id = id;
11575         frame.name = id;
11576         frame.className = 'x-hidden';
11577         if(Roo.isIE){
11578             frame.src = Roo.SSL_SECURE_URL;
11579         }
11580         document.body.appendChild(frame);
11581
11582         if(Roo.isIE){
11583            document.frames[id].name = id;
11584         }
11585
11586         var form = Roo.getDom(o.form);
11587         form.target = id;
11588         form.method = 'POST';
11589         form.enctype = form.encoding = 'multipart/form-data';
11590         if(url){
11591             form.action = url;
11592         }
11593
11594         var hiddens, hd;
11595         if(ps){ // add dynamic params
11596             hiddens = [];
11597             ps = Roo.urlDecode(ps, false);
11598             for(var k in ps){
11599                 if(ps.hasOwnProperty(k)){
11600                     hd = document.createElement('input');
11601                     hd.type = 'hidden';
11602                     hd.name = k;
11603                     hd.value = ps[k];
11604                     form.appendChild(hd);
11605                     hiddens.push(hd);
11606                 }
11607             }
11608         }
11609
11610         function cb(){
11611             var r = {  // bogus response object
11612                 responseText : '',
11613                 responseXML : null
11614             };
11615
11616             r.argument = o ? o.argument : null;
11617
11618             try { //
11619                 var doc;
11620                 if(Roo.isIE){
11621                     doc = frame.contentWindow.document;
11622                 }else {
11623                     doc = (frame.contentDocument || window.frames[id].document);
11624                 }
11625                 if(doc && doc.body){
11626                     r.responseText = doc.body.innerHTML;
11627                 }
11628                 if(doc && doc.XMLDocument){
11629                     r.responseXML = doc.XMLDocument;
11630                 }else {
11631                     r.responseXML = doc;
11632                 }
11633             }
11634             catch(e) {
11635                 // ignore
11636             }
11637
11638             Roo.EventManager.removeListener(frame, 'load', cb, this);
11639
11640             this.fireEvent("requestcomplete", this, r, o);
11641             Roo.callback(o.success, o.scope, [r, o]);
11642             Roo.callback(o.callback, o.scope, [o, true, r]);
11643
11644             setTimeout(function(){document.body.removeChild(frame);}, 100);
11645         }
11646
11647         Roo.EventManager.on(frame, 'load', cb, this);
11648         form.submit();
11649
11650         if(hiddens){ // remove dynamic params
11651             for(var i = 0, len = hiddens.length; i < len; i++){
11652                 form.removeChild(hiddens[i]);
11653             }
11654         }
11655     }
11656 });
11657 /*
11658  * Based on:
11659  * Ext JS Library 1.1.1
11660  * Copyright(c) 2006-2007, Ext JS, LLC.
11661  *
11662  * Originally Released Under LGPL - original licence link has changed is not relivant.
11663  *
11664  * Fork - LGPL
11665  * <script type="text/javascript">
11666  */
11667  
11668 /**
11669  * Global Ajax request class.
11670  * 
11671  * @class Roo.Ajax
11672  * @extends Roo.data.Connection
11673  * @static
11674  * 
11675  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11676  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11677  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11678  * @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)
11679  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11680  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11681  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11682  */
11683 Roo.Ajax = new Roo.data.Connection({
11684     // fix up the docs
11685     /**
11686      * @scope Roo.Ajax
11687      * @type {Boolear} 
11688      */
11689     autoAbort : false,
11690
11691     /**
11692      * Serialize the passed form into a url encoded string
11693      * @scope Roo.Ajax
11694      * @param {String/HTMLElement} form
11695      * @return {String}
11696      */
11697     serializeForm : function(form){
11698         return Roo.lib.Ajax.serializeForm(form);
11699     }
11700 });/*
11701  * Based on:
11702  * Ext JS Library 1.1.1
11703  * Copyright(c) 2006-2007, Ext JS, LLC.
11704  *
11705  * Originally Released Under LGPL - original licence link has changed is not relivant.
11706  *
11707  * Fork - LGPL
11708  * <script type="text/javascript">
11709  */
11710
11711  
11712 /**
11713  * @class Roo.UpdateManager
11714  * @extends Roo.util.Observable
11715  * Provides AJAX-style update for Element object.<br><br>
11716  * Usage:<br>
11717  * <pre><code>
11718  * // Get it from a Roo.Element object
11719  * var el = Roo.get("foo");
11720  * var mgr = el.getUpdateManager();
11721  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11722  * ...
11723  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11724  * <br>
11725  * // or directly (returns the same UpdateManager instance)
11726  * var mgr = new Roo.UpdateManager("myElementId");
11727  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11728  * mgr.on("update", myFcnNeedsToKnow);
11729  * <br>
11730    // short handed call directly from the element object
11731    Roo.get("foo").load({
11732         url: "bar.php",
11733         scripts:true,
11734         params: "for=bar",
11735         text: "Loading Foo..."
11736    });
11737  * </code></pre>
11738  * @constructor
11739  * Create new UpdateManager directly.
11740  * @param {String/HTMLElement/Roo.Element} el The element to update
11741  * @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).
11742  */
11743 Roo.UpdateManager = function(el, forceNew){
11744     el = Roo.get(el);
11745     if(!forceNew && el.updateManager){
11746         return el.updateManager;
11747     }
11748     /**
11749      * The Element object
11750      * @type Roo.Element
11751      */
11752     this.el = el;
11753     /**
11754      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11755      * @type String
11756      */
11757     this.defaultUrl = null;
11758
11759     this.addEvents({
11760         /**
11761          * @event beforeupdate
11762          * Fired before an update is made, return false from your handler and the update is cancelled.
11763          * @param {Roo.Element} el
11764          * @param {String/Object/Function} url
11765          * @param {String/Object} params
11766          */
11767         "beforeupdate": true,
11768         /**
11769          * @event update
11770          * Fired after successful update is made.
11771          * @param {Roo.Element} el
11772          * @param {Object} oResponseObject The response Object
11773          */
11774         "update": true,
11775         /**
11776          * @event failure
11777          * Fired on update failure.
11778          * @param {Roo.Element} el
11779          * @param {Object} oResponseObject The response Object
11780          */
11781         "failure": true
11782     });
11783     var d = Roo.UpdateManager.defaults;
11784     /**
11785      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11786      * @type String
11787      */
11788     this.sslBlankUrl = d.sslBlankUrl;
11789     /**
11790      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11791      * @type Boolean
11792      */
11793     this.disableCaching = d.disableCaching;
11794     /**
11795      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11796      * @type String
11797      */
11798     this.indicatorText = d.indicatorText;
11799     /**
11800      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11801      * @type String
11802      */
11803     this.showLoadIndicator = d.showLoadIndicator;
11804     /**
11805      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11806      * @type Number
11807      */
11808     this.timeout = d.timeout;
11809
11810     /**
11811      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11812      * @type Boolean
11813      */
11814     this.loadScripts = d.loadScripts;
11815
11816     /**
11817      * Transaction object of current executing transaction
11818      */
11819     this.transaction = null;
11820
11821     /**
11822      * @private
11823      */
11824     this.autoRefreshProcId = null;
11825     /**
11826      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11827      * @type Function
11828      */
11829     this.refreshDelegate = this.refresh.createDelegate(this);
11830     /**
11831      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11832      * @type Function
11833      */
11834     this.updateDelegate = this.update.createDelegate(this);
11835     /**
11836      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11837      * @type Function
11838      */
11839     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11840     /**
11841      * @private
11842      */
11843     this.successDelegate = this.processSuccess.createDelegate(this);
11844     /**
11845      * @private
11846      */
11847     this.failureDelegate = this.processFailure.createDelegate(this);
11848
11849     if(!this.renderer){
11850      /**
11851       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11852       */
11853     this.renderer = new Roo.UpdateManager.BasicRenderer();
11854     }
11855     
11856     Roo.UpdateManager.superclass.constructor.call(this);
11857 };
11858
11859 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11860     /**
11861      * Get the Element this UpdateManager is bound to
11862      * @return {Roo.Element} The element
11863      */
11864     getEl : function(){
11865         return this.el;
11866     },
11867     /**
11868      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11869      * @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:
11870 <pre><code>
11871 um.update({<br/>
11872     url: "your-url.php",<br/>
11873     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11874     callback: yourFunction,<br/>
11875     scope: yourObject, //(optional scope)  <br/>
11876     discardUrl: false, <br/>
11877     nocache: false,<br/>
11878     text: "Loading...",<br/>
11879     timeout: 30,<br/>
11880     scripts: false<br/>
11881 });
11882 </code></pre>
11883      * The only required property is url. The optional properties nocache, text and scripts
11884      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11885      * @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}
11886      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11887      * @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.
11888      */
11889     update : function(url, params, callback, discardUrl){
11890         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11891             var method = this.method,
11892                 cfg;
11893             if(typeof url == "object"){ // must be config object
11894                 cfg = url;
11895                 url = cfg.url;
11896                 params = params || cfg.params;
11897                 callback = callback || cfg.callback;
11898                 discardUrl = discardUrl || cfg.discardUrl;
11899                 if(callback && cfg.scope){
11900                     callback = callback.createDelegate(cfg.scope);
11901                 }
11902                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11903                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11904                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11905                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11906                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11907             }
11908             this.showLoading();
11909             if(!discardUrl){
11910                 this.defaultUrl = url;
11911             }
11912             if(typeof url == "function"){
11913                 url = url.call(this);
11914             }
11915
11916             method = method || (params ? "POST" : "GET");
11917             if(method == "GET"){
11918                 url = this.prepareUrl(url);
11919             }
11920
11921             var o = Roo.apply(cfg ||{}, {
11922                 url : url,
11923                 params: params,
11924                 success: this.successDelegate,
11925                 failure: this.failureDelegate,
11926                 callback: undefined,
11927                 timeout: (this.timeout*1000),
11928                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11929             });
11930             Roo.log("updated manager called with timeout of " + o.timeout);
11931             this.transaction = Roo.Ajax.request(o);
11932         }
11933     },
11934
11935     /**
11936      * 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.
11937      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11938      * @param {String/HTMLElement} form The form Id or form element
11939      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11940      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11941      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11942      */
11943     formUpdate : function(form, url, reset, callback){
11944         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11945             if(typeof url == "function"){
11946                 url = url.call(this);
11947             }
11948             form = Roo.getDom(form);
11949             this.transaction = Roo.Ajax.request({
11950                 form: form,
11951                 url:url,
11952                 success: this.successDelegate,
11953                 failure: this.failureDelegate,
11954                 timeout: (this.timeout*1000),
11955                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11956             });
11957             this.showLoading.defer(1, this);
11958         }
11959     },
11960
11961     /**
11962      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11963      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11964      */
11965     refresh : function(callback){
11966         if(this.defaultUrl == null){
11967             return;
11968         }
11969         this.update(this.defaultUrl, null, callback, true);
11970     },
11971
11972     /**
11973      * Set this element to auto refresh.
11974      * @param {Number} interval How often to update (in seconds).
11975      * @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)
11976      * @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}
11977      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11978      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11979      */
11980     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11981         if(refreshNow){
11982             this.update(url || this.defaultUrl, params, callback, true);
11983         }
11984         if(this.autoRefreshProcId){
11985             clearInterval(this.autoRefreshProcId);
11986         }
11987         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11988     },
11989
11990     /**
11991      * Stop auto refresh on this element.
11992      */
11993      stopAutoRefresh : function(){
11994         if(this.autoRefreshProcId){
11995             clearInterval(this.autoRefreshProcId);
11996             delete this.autoRefreshProcId;
11997         }
11998     },
11999
12000     isAutoRefreshing : function(){
12001        return this.autoRefreshProcId ? true : false;
12002     },
12003     /**
12004      * Called to update the element to "Loading" state. Override to perform custom action.
12005      */
12006     showLoading : function(){
12007         if(this.showLoadIndicator){
12008             this.el.update(this.indicatorText);
12009         }
12010     },
12011
12012     /**
12013      * Adds unique parameter to query string if disableCaching = true
12014      * @private
12015      */
12016     prepareUrl : function(url){
12017         if(this.disableCaching){
12018             var append = "_dc=" + (new Date().getTime());
12019             if(url.indexOf("?") !== -1){
12020                 url += "&" + append;
12021             }else{
12022                 url += "?" + append;
12023             }
12024         }
12025         return url;
12026     },
12027
12028     /**
12029      * @private
12030      */
12031     processSuccess : function(response){
12032         this.transaction = null;
12033         if(response.argument.form && response.argument.reset){
12034             try{ // put in try/catch since some older FF releases had problems with this
12035                 response.argument.form.reset();
12036             }catch(e){}
12037         }
12038         if(this.loadScripts){
12039             this.renderer.render(this.el, response, this,
12040                 this.updateComplete.createDelegate(this, [response]));
12041         }else{
12042             this.renderer.render(this.el, response, this);
12043             this.updateComplete(response);
12044         }
12045     },
12046
12047     updateComplete : function(response){
12048         this.fireEvent("update", this.el, response);
12049         if(typeof response.argument.callback == "function"){
12050             response.argument.callback(this.el, true, response);
12051         }
12052     },
12053
12054     /**
12055      * @private
12056      */
12057     processFailure : function(response){
12058         this.transaction = null;
12059         this.fireEvent("failure", this.el, response);
12060         if(typeof response.argument.callback == "function"){
12061             response.argument.callback(this.el, false, response);
12062         }
12063     },
12064
12065     /**
12066      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12067      * @param {Object} renderer The object implementing the render() method
12068      */
12069     setRenderer : function(renderer){
12070         this.renderer = renderer;
12071     },
12072
12073     getRenderer : function(){
12074        return this.renderer;
12075     },
12076
12077     /**
12078      * Set the defaultUrl used for updates
12079      * @param {String/Function} defaultUrl The url or a function to call to get the url
12080      */
12081     setDefaultUrl : function(defaultUrl){
12082         this.defaultUrl = defaultUrl;
12083     },
12084
12085     /**
12086      * Aborts the executing transaction
12087      */
12088     abort : function(){
12089         if(this.transaction){
12090             Roo.Ajax.abort(this.transaction);
12091         }
12092     },
12093
12094     /**
12095      * Returns true if an update is in progress
12096      * @return {Boolean}
12097      */
12098     isUpdating : function(){
12099         if(this.transaction){
12100             return Roo.Ajax.isLoading(this.transaction);
12101         }
12102         return false;
12103     }
12104 });
12105
12106 /**
12107  * @class Roo.UpdateManager.defaults
12108  * @static (not really - but it helps the doc tool)
12109  * The defaults collection enables customizing the default properties of UpdateManager
12110  */
12111    Roo.UpdateManager.defaults = {
12112        /**
12113          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12114          * @type Number
12115          */
12116          timeout : 30,
12117
12118          /**
12119          * True to process scripts by default (Defaults to false).
12120          * @type Boolean
12121          */
12122         loadScripts : false,
12123
12124         /**
12125         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12126         * @type String
12127         */
12128         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12129         /**
12130          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12131          * @type Boolean
12132          */
12133         disableCaching : false,
12134         /**
12135          * Whether to show indicatorText when loading (Defaults to true).
12136          * @type Boolean
12137          */
12138         showLoadIndicator : true,
12139         /**
12140          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12141          * @type String
12142          */
12143         indicatorText : '<div class="loading-indicator">Loading...</div>'
12144    };
12145
12146 /**
12147  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12148  *Usage:
12149  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12150  * @param {String/HTMLElement/Roo.Element} el The element to update
12151  * @param {String} url The url
12152  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12153  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12154  * @static
12155  * @deprecated
12156  * @member Roo.UpdateManager
12157  */
12158 Roo.UpdateManager.updateElement = function(el, url, params, options){
12159     var um = Roo.get(el, true).getUpdateManager();
12160     Roo.apply(um, options);
12161     um.update(url, params, options ? options.callback : null);
12162 };
12163 // alias for backwards compat
12164 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12165 /**
12166  * @class Roo.UpdateManager.BasicRenderer
12167  * Default Content renderer. Updates the elements innerHTML with the responseText.
12168  */
12169 Roo.UpdateManager.BasicRenderer = function(){};
12170
12171 Roo.UpdateManager.BasicRenderer.prototype = {
12172     /**
12173      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12174      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12175      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12176      * @param {Roo.Element} el The element being rendered
12177      * @param {Object} response The YUI Connect response object
12178      * @param {UpdateManager} updateManager The calling update manager
12179      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12180      */
12181      render : function(el, response, updateManager, callback){
12182         el.update(response.responseText, updateManager.loadScripts, callback);
12183     }
12184 };
12185 /*
12186  * Based on:
12187  * Roo JS
12188  * (c)) Alan Knowles
12189  * Licence : LGPL
12190  */
12191
12192
12193 /**
12194  * @class Roo.DomTemplate
12195  * @extends Roo.Template
12196  * An effort at a dom based template engine..
12197  *
12198  * Similar to XTemplate, except it uses dom parsing to create the template..
12199  *
12200  * Supported features:
12201  *
12202  *  Tags:
12203
12204 <pre><code>
12205       {a_variable} - output encoded.
12206       {a_variable.format:("Y-m-d")} - call a method on the variable
12207       {a_variable:raw} - unencoded output
12208       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12209       {a_variable:this.method_on_template(...)} - call a method on the template object.
12210  
12211 </code></pre>
12212  *  The tpl tag:
12213 <pre><code>
12214         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12215         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12216         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12217         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12218   
12219 </code></pre>
12220  *      
12221  */
12222 Roo.DomTemplate = function()
12223 {
12224      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12225      if (this.html) {
12226         this.compile();
12227      }
12228 };
12229
12230
12231 Roo.extend(Roo.DomTemplate, Roo.Template, {
12232     /**
12233      * id counter for sub templates.
12234      */
12235     id : 0,
12236     /**
12237      * flag to indicate if dom parser is inside a pre,
12238      * it will strip whitespace if not.
12239      */
12240     inPre : false,
12241     
12242     /**
12243      * The various sub templates
12244      */
12245     tpls : false,
12246     
12247     
12248     
12249     /**
12250      *
12251      * basic tag replacing syntax
12252      * WORD:WORD()
12253      *
12254      * // you can fake an object call by doing this
12255      *  x.t:(test,tesT) 
12256      * 
12257      */
12258     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12259     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12260     
12261     iterChild : function (node, method) {
12262         
12263         var oldPre = this.inPre;
12264         if (node.tagName == 'PRE') {
12265             this.inPre = true;
12266         }
12267         for( var i = 0; i < node.childNodes.length; i++) {
12268             method.call(this, node.childNodes[i]);
12269         }
12270         this.inPre = oldPre;
12271     },
12272     
12273     
12274     
12275     /**
12276      * compile the template
12277      *
12278      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12279      *
12280      */
12281     compile: function()
12282     {
12283         var s = this.html;
12284         
12285         // covert the html into DOM...
12286         var doc = false;
12287         var div =false;
12288         try {
12289             doc = document.implementation.createHTMLDocument("");
12290             doc.documentElement.innerHTML =   this.html  ;
12291             div = doc.documentElement;
12292         } catch (e) {
12293             // old IE... - nasty -- it causes all sorts of issues.. with
12294             // images getting pulled from server..
12295             div = document.createElement('div');
12296             div.innerHTML = this.html;
12297         }
12298         //doc.documentElement.innerHTML = htmlBody
12299          
12300         
12301         
12302         this.tpls = [];
12303         var _t = this;
12304         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12305         
12306         var tpls = this.tpls;
12307         
12308         // create a top level template from the snippet..
12309         
12310         //Roo.log(div.innerHTML);
12311         
12312         var tpl = {
12313             uid : 'master',
12314             id : this.id++,
12315             attr : false,
12316             value : false,
12317             body : div.innerHTML,
12318             
12319             forCall : false,
12320             execCall : false,
12321             dom : div,
12322             isTop : true
12323             
12324         };
12325         tpls.unshift(tpl);
12326         
12327         
12328         // compile them...
12329         this.tpls = [];
12330         Roo.each(tpls, function(tp){
12331             this.compileTpl(tp);
12332             this.tpls[tp.id] = tp;
12333         }, this);
12334         
12335         this.master = tpls[0];
12336         return this;
12337         
12338         
12339     },
12340     
12341     compileNode : function(node, istop) {
12342         // test for
12343         //Roo.log(node);
12344         
12345         
12346         // skip anything not a tag..
12347         if (node.nodeType != 1) {
12348             if (node.nodeType == 3 && !this.inPre) {
12349                 // reduce white space..
12350                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12351                 
12352             }
12353             return;
12354         }
12355         
12356         var tpl = {
12357             uid : false,
12358             id : false,
12359             attr : false,
12360             value : false,
12361             body : '',
12362             
12363             forCall : false,
12364             execCall : false,
12365             dom : false,
12366             isTop : istop
12367             
12368             
12369         };
12370         
12371         
12372         switch(true) {
12373             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12374             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12375             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12376             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12377             // no default..
12378         }
12379         
12380         
12381         if (!tpl.attr) {
12382             // just itterate children..
12383             this.iterChild(node,this.compileNode);
12384             return;
12385         }
12386         tpl.uid = this.id++;
12387         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12388         node.removeAttribute('roo-'+ tpl.attr);
12389         if (tpl.attr != 'name') {
12390             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12391             node.parentNode.replaceChild(placeholder,  node);
12392         } else {
12393             
12394             var placeholder =  document.createElement('span');
12395             placeholder.className = 'roo-tpl-' + tpl.value;
12396             node.parentNode.replaceChild(placeholder,  node);
12397         }
12398         
12399         // parent now sees '{domtplXXXX}
12400         this.iterChild(node,this.compileNode);
12401         
12402         // we should now have node body...
12403         var div = document.createElement('div');
12404         div.appendChild(node);
12405         tpl.dom = node;
12406         // this has the unfortunate side effect of converting tagged attributes
12407         // eg. href="{...}" into %7C...%7D
12408         // this has been fixed by searching for those combo's although it's a bit hacky..
12409         
12410         
12411         tpl.body = div.innerHTML;
12412         
12413         
12414          
12415         tpl.id = tpl.uid;
12416         switch(tpl.attr) {
12417             case 'for' :
12418                 switch (tpl.value) {
12419                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12420                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12421                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12422                 }
12423                 break;
12424             
12425             case 'exec':
12426                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12427                 break;
12428             
12429             case 'if':     
12430                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12431                 break;
12432             
12433             case 'name':
12434                 tpl.id  = tpl.value; // replace non characters???
12435                 break;
12436             
12437         }
12438         
12439         
12440         this.tpls.push(tpl);
12441         
12442         
12443         
12444     },
12445     
12446     
12447     
12448     
12449     /**
12450      * Compile a segment of the template into a 'sub-template'
12451      *
12452      * 
12453      * 
12454      *
12455      */
12456     compileTpl : function(tpl)
12457     {
12458         var fm = Roo.util.Format;
12459         var useF = this.disableFormats !== true;
12460         
12461         var sep = Roo.isGecko ? "+\n" : ",\n";
12462         
12463         var undef = function(str) {
12464             Roo.debug && Roo.log("Property not found :"  + str);
12465             return '';
12466         };
12467           
12468         //Roo.log(tpl.body);
12469         
12470         
12471         
12472         var fn = function(m, lbrace, name, format, args)
12473         {
12474             //Roo.log("ARGS");
12475             //Roo.log(arguments);
12476             args = args ? args.replace(/\\'/g,"'") : args;
12477             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12478             if (typeof(format) == 'undefined') {
12479                 format =  'htmlEncode'; 
12480             }
12481             if (format == 'raw' ) {
12482                 format = false;
12483             }
12484             
12485             if(name.substr(0, 6) == 'domtpl'){
12486                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12487             }
12488             
12489             // build an array of options to determine if value is undefined..
12490             
12491             // basically get 'xxxx.yyyy' then do
12492             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12493             //    (function () { Roo.log("Property not found"); return ''; })() :
12494             //    ......
12495             
12496             var udef_ar = [];
12497             var lookfor = '';
12498             Roo.each(name.split('.'), function(st) {
12499                 lookfor += (lookfor.length ? '.': '') + st;
12500                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12501             });
12502             
12503             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12504             
12505             
12506             if(format && useF){
12507                 
12508                 args = args ? ',' + args : "";
12509                  
12510                 if(format.substr(0, 5) != "this."){
12511                     format = "fm." + format + '(';
12512                 }else{
12513                     format = 'this.call("'+ format.substr(5) + '", ';
12514                     args = ", values";
12515                 }
12516                 
12517                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12518             }
12519              
12520             if (args && args.length) {
12521                 // called with xxyx.yuu:(test,test)
12522                 // change to ()
12523                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12524             }
12525             // raw.. - :raw modifier..
12526             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12527             
12528         };
12529         var body;
12530         // branched to use + in gecko and [].join() in others
12531         if(Roo.isGecko){
12532             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12533                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12534                     "';};};";
12535         }else{
12536             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12537             body.push(tpl.body.replace(/(\r\n|\n)/g,
12538                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12539             body.push("'].join('');};};");
12540             body = body.join('');
12541         }
12542         
12543         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12544        
12545         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12546         eval(body);
12547         
12548         return this;
12549     },
12550      
12551     /**
12552      * same as applyTemplate, except it's done to one of the subTemplates
12553      * when using named templates, you can do:
12554      *
12555      * var str = pl.applySubTemplate('your-name', values);
12556      *
12557      * 
12558      * @param {Number} id of the template
12559      * @param {Object} values to apply to template
12560      * @param {Object} parent (normaly the instance of this object)
12561      */
12562     applySubTemplate : function(id, values, parent)
12563     {
12564         
12565         
12566         var t = this.tpls[id];
12567         
12568         
12569         try { 
12570             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12571                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12572                 return '';
12573             }
12574         } catch(e) {
12575             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12576             Roo.log(values);
12577           
12578             return '';
12579         }
12580         try { 
12581             
12582             if(t.execCall && t.execCall.call(this, values, parent)){
12583                 return '';
12584             }
12585         } catch(e) {
12586             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12587             Roo.log(values);
12588             return '';
12589         }
12590         
12591         try {
12592             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12593             parent = t.target ? values : parent;
12594             if(t.forCall && vs instanceof Array){
12595                 var buf = [];
12596                 for(var i = 0, len = vs.length; i < len; i++){
12597                     try {
12598                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12599                     } catch (e) {
12600                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12601                         Roo.log(e.body);
12602                         //Roo.log(t.compiled);
12603                         Roo.log(vs[i]);
12604                     }   
12605                 }
12606                 return buf.join('');
12607             }
12608         } catch (e) {
12609             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12610             Roo.log(values);
12611             return '';
12612         }
12613         try {
12614             return t.compiled.call(this, vs, parent);
12615         } catch (e) {
12616             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12617             Roo.log(e.body);
12618             //Roo.log(t.compiled);
12619             Roo.log(values);
12620             return '';
12621         }
12622     },
12623
12624    
12625
12626     applyTemplate : function(values){
12627         return this.master.compiled.call(this, values, {});
12628         //var s = this.subs;
12629     },
12630
12631     apply : function(){
12632         return this.applyTemplate.apply(this, arguments);
12633     }
12634
12635  });
12636
12637 Roo.DomTemplate.from = function(el){
12638     el = Roo.getDom(el);
12639     return new Roo.Domtemplate(el.value || el.innerHTML);
12640 };/*
12641  * Based on:
12642  * Ext JS Library 1.1.1
12643  * Copyright(c) 2006-2007, Ext JS, LLC.
12644  *
12645  * Originally Released Under LGPL - original licence link has changed is not relivant.
12646  *
12647  * Fork - LGPL
12648  * <script type="text/javascript">
12649  */
12650
12651 /**
12652  * @class Roo.util.DelayedTask
12653  * Provides a convenient method of performing setTimeout where a new
12654  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12655  * You can use this class to buffer
12656  * the keypress events for a certain number of milliseconds, and perform only if they stop
12657  * for that amount of time.
12658  * @constructor The parameters to this constructor serve as defaults and are not required.
12659  * @param {Function} fn (optional) The default function to timeout
12660  * @param {Object} scope (optional) The default scope of that timeout
12661  * @param {Array} args (optional) The default Array of arguments
12662  */
12663 Roo.util.DelayedTask = function(fn, scope, args){
12664     var id = null, d, t;
12665
12666     var call = function(){
12667         var now = new Date().getTime();
12668         if(now - t >= d){
12669             clearInterval(id);
12670             id = null;
12671             fn.apply(scope, args || []);
12672         }
12673     };
12674     /**
12675      * Cancels any pending timeout and queues a new one
12676      * @param {Number} delay The milliseconds to delay
12677      * @param {Function} newFn (optional) Overrides function passed to constructor
12678      * @param {Object} newScope (optional) Overrides scope passed to constructor
12679      * @param {Array} newArgs (optional) Overrides args passed to constructor
12680      */
12681     this.delay = function(delay, newFn, newScope, newArgs){
12682         if(id && delay != d){
12683             this.cancel();
12684         }
12685         d = delay;
12686         t = new Date().getTime();
12687         fn = newFn || fn;
12688         scope = newScope || scope;
12689         args = newArgs || args;
12690         if(!id){
12691             id = setInterval(call, d);
12692         }
12693     };
12694
12695     /**
12696      * Cancel the last queued timeout
12697      */
12698     this.cancel = function(){
12699         if(id){
12700             clearInterval(id);
12701             id = null;
12702         }
12703     };
12704 };/*
12705  * Based on:
12706  * Ext JS Library 1.1.1
12707  * Copyright(c) 2006-2007, Ext JS, LLC.
12708  *
12709  * Originally Released Under LGPL - original licence link has changed is not relivant.
12710  *
12711  * Fork - LGPL
12712  * <script type="text/javascript">
12713  */
12714  
12715  
12716 Roo.util.TaskRunner = function(interval){
12717     interval = interval || 10;
12718     var tasks = [], removeQueue = [];
12719     var id = 0;
12720     var running = false;
12721
12722     var stopThread = function(){
12723         running = false;
12724         clearInterval(id);
12725         id = 0;
12726     };
12727
12728     var startThread = function(){
12729         if(!running){
12730             running = true;
12731             id = setInterval(runTasks, interval);
12732         }
12733     };
12734
12735     var removeTask = function(task){
12736         removeQueue.push(task);
12737         if(task.onStop){
12738             task.onStop();
12739         }
12740     };
12741
12742     var runTasks = function(){
12743         if(removeQueue.length > 0){
12744             for(var i = 0, len = removeQueue.length; i < len; i++){
12745                 tasks.remove(removeQueue[i]);
12746             }
12747             removeQueue = [];
12748             if(tasks.length < 1){
12749                 stopThread();
12750                 return;
12751             }
12752         }
12753         var now = new Date().getTime();
12754         for(var i = 0, len = tasks.length; i < len; ++i){
12755             var t = tasks[i];
12756             var itime = now - t.taskRunTime;
12757             if(t.interval <= itime){
12758                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12759                 t.taskRunTime = now;
12760                 if(rt === false || t.taskRunCount === t.repeat){
12761                     removeTask(t);
12762                     return;
12763                 }
12764             }
12765             if(t.duration && t.duration <= (now - t.taskStartTime)){
12766                 removeTask(t);
12767             }
12768         }
12769     };
12770
12771     /**
12772      * Queues a new task.
12773      * @param {Object} task
12774      */
12775     this.start = function(task){
12776         tasks.push(task);
12777         task.taskStartTime = new Date().getTime();
12778         task.taskRunTime = 0;
12779         task.taskRunCount = 0;
12780         startThread();
12781         return task;
12782     };
12783
12784     this.stop = function(task){
12785         removeTask(task);
12786         return task;
12787     };
12788
12789     this.stopAll = function(){
12790         stopThread();
12791         for(var i = 0, len = tasks.length; i < len; i++){
12792             if(tasks[i].onStop){
12793                 tasks[i].onStop();
12794             }
12795         }
12796         tasks = [];
12797         removeQueue = [];
12798     };
12799 };
12800
12801 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12802  * Based on:
12803  * Ext JS Library 1.1.1
12804  * Copyright(c) 2006-2007, Ext JS, LLC.
12805  *
12806  * Originally Released Under LGPL - original licence link has changed is not relivant.
12807  *
12808  * Fork - LGPL
12809  * <script type="text/javascript">
12810  */
12811
12812  
12813 /**
12814  * @class Roo.util.MixedCollection
12815  * @extends Roo.util.Observable
12816  * A Collection class that maintains both numeric indexes and keys and exposes events.
12817  * @constructor
12818  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12819  * collection (defaults to false)
12820  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12821  * and return the key value for that item.  This is used when available to look up the key on items that
12822  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12823  * equivalent to providing an implementation for the {@link #getKey} method.
12824  */
12825 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12826     this.items = [];
12827     this.map = {};
12828     this.keys = [];
12829     this.length = 0;
12830     this.addEvents({
12831         /**
12832          * @event clear
12833          * Fires when the collection is cleared.
12834          */
12835         "clear" : true,
12836         /**
12837          * @event add
12838          * Fires when an item is added to the collection.
12839          * @param {Number} index The index at which the item was added.
12840          * @param {Object} o The item added.
12841          * @param {String} key The key associated with the added item.
12842          */
12843         "add" : true,
12844         /**
12845          * @event replace
12846          * Fires when an item is replaced in the collection.
12847          * @param {String} key he key associated with the new added.
12848          * @param {Object} old The item being replaced.
12849          * @param {Object} new The new item.
12850          */
12851         "replace" : true,
12852         /**
12853          * @event remove
12854          * Fires when an item is removed from the collection.
12855          * @param {Object} o The item being removed.
12856          * @param {String} key (optional) The key associated with the removed item.
12857          */
12858         "remove" : true,
12859         "sort" : true
12860     });
12861     this.allowFunctions = allowFunctions === true;
12862     if(keyFn){
12863         this.getKey = keyFn;
12864     }
12865     Roo.util.MixedCollection.superclass.constructor.call(this);
12866 };
12867
12868 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12869     allowFunctions : false,
12870     
12871 /**
12872  * Adds an item to the collection.
12873  * @param {String} key The key to associate with the item
12874  * @param {Object} o The item to add.
12875  * @return {Object} The item added.
12876  */
12877     add : function(key, o){
12878         if(arguments.length == 1){
12879             o = arguments[0];
12880             key = this.getKey(o);
12881         }
12882         if(typeof key == "undefined" || key === null){
12883             this.length++;
12884             this.items.push(o);
12885             this.keys.push(null);
12886         }else{
12887             var old = this.map[key];
12888             if(old){
12889                 return this.replace(key, o);
12890             }
12891             this.length++;
12892             this.items.push(o);
12893             this.map[key] = o;
12894             this.keys.push(key);
12895         }
12896         this.fireEvent("add", this.length-1, o, key);
12897         return o;
12898     },
12899        
12900 /**
12901   * MixedCollection has a generic way to fetch keys if you implement getKey.
12902 <pre><code>
12903 // normal way
12904 var mc = new Roo.util.MixedCollection();
12905 mc.add(someEl.dom.id, someEl);
12906 mc.add(otherEl.dom.id, otherEl);
12907 //and so on
12908
12909 // using getKey
12910 var mc = new Roo.util.MixedCollection();
12911 mc.getKey = function(el){
12912    return el.dom.id;
12913 };
12914 mc.add(someEl);
12915 mc.add(otherEl);
12916
12917 // or via the constructor
12918 var mc = new Roo.util.MixedCollection(false, function(el){
12919    return el.dom.id;
12920 });
12921 mc.add(someEl);
12922 mc.add(otherEl);
12923 </code></pre>
12924  * @param o {Object} The item for which to find the key.
12925  * @return {Object} The key for the passed item.
12926  */
12927     getKey : function(o){
12928          return o.id; 
12929     },
12930    
12931 /**
12932  * Replaces an item in the collection.
12933  * @param {String} key The key associated with the item to replace, or the item to replace.
12934  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12935  * @return {Object}  The new item.
12936  */
12937     replace : function(key, o){
12938         if(arguments.length == 1){
12939             o = arguments[0];
12940             key = this.getKey(o);
12941         }
12942         var old = this.item(key);
12943         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12944              return this.add(key, o);
12945         }
12946         var index = this.indexOfKey(key);
12947         this.items[index] = o;
12948         this.map[key] = o;
12949         this.fireEvent("replace", key, old, o);
12950         return o;
12951     },
12952    
12953 /**
12954  * Adds all elements of an Array or an Object to the collection.
12955  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12956  * an Array of values, each of which are added to the collection.
12957  */
12958     addAll : function(objs){
12959         if(arguments.length > 1 || objs instanceof Array){
12960             var args = arguments.length > 1 ? arguments : objs;
12961             for(var i = 0, len = args.length; i < len; i++){
12962                 this.add(args[i]);
12963             }
12964         }else{
12965             for(var key in objs){
12966                 if(this.allowFunctions || typeof objs[key] != "function"){
12967                     this.add(key, objs[key]);
12968                 }
12969             }
12970         }
12971     },
12972    
12973 /**
12974  * Executes the specified function once for every item in the collection, passing each
12975  * item as the first and only parameter. returning false from the function will stop the iteration.
12976  * @param {Function} fn The function to execute for each item.
12977  * @param {Object} scope (optional) The scope in which to execute the function.
12978  */
12979     each : function(fn, scope){
12980         var items = [].concat(this.items); // each safe for removal
12981         for(var i = 0, len = items.length; i < len; i++){
12982             if(fn.call(scope || items[i], items[i], i, len) === false){
12983                 break;
12984             }
12985         }
12986     },
12987    
12988 /**
12989  * Executes the specified function once for every key in the collection, passing each
12990  * key, and its associated item as the first two parameters.
12991  * @param {Function} fn The function to execute for each item.
12992  * @param {Object} scope (optional) The scope in which to execute the function.
12993  */
12994     eachKey : function(fn, scope){
12995         for(var i = 0, len = this.keys.length; i < len; i++){
12996             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12997         }
12998     },
12999    
13000 /**
13001  * Returns the first item in the collection which elicits a true return value from the
13002  * passed selection function.
13003  * @param {Function} fn The selection function to execute for each item.
13004  * @param {Object} scope (optional) The scope in which to execute the function.
13005  * @return {Object} The first item in the collection which returned true from the selection function.
13006  */
13007     find : function(fn, scope){
13008         for(var i = 0, len = this.items.length; i < len; i++){
13009             if(fn.call(scope || window, this.items[i], this.keys[i])){
13010                 return this.items[i];
13011             }
13012         }
13013         return null;
13014     },
13015    
13016 /**
13017  * Inserts an item at the specified index in the collection.
13018  * @param {Number} index The index to insert the item at.
13019  * @param {String} key The key to associate with the new item, or the item itself.
13020  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13021  * @return {Object} The item inserted.
13022  */
13023     insert : function(index, key, o){
13024         if(arguments.length == 2){
13025             o = arguments[1];
13026             key = this.getKey(o);
13027         }
13028         if(index >= this.length){
13029             return this.add(key, o);
13030         }
13031         this.length++;
13032         this.items.splice(index, 0, o);
13033         if(typeof key != "undefined" && key != null){
13034             this.map[key] = o;
13035         }
13036         this.keys.splice(index, 0, key);
13037         this.fireEvent("add", index, o, key);
13038         return o;
13039     },
13040    
13041 /**
13042  * Removed an item from the collection.
13043  * @param {Object} o The item to remove.
13044  * @return {Object} The item removed.
13045  */
13046     remove : function(o){
13047         return this.removeAt(this.indexOf(o));
13048     },
13049    
13050 /**
13051  * Remove an item from a specified index in the collection.
13052  * @param {Number} index The index within the collection of the item to remove.
13053  */
13054     removeAt : function(index){
13055         if(index < this.length && index >= 0){
13056             this.length--;
13057             var o = this.items[index];
13058             this.items.splice(index, 1);
13059             var key = this.keys[index];
13060             if(typeof key != "undefined"){
13061                 delete this.map[key];
13062             }
13063             this.keys.splice(index, 1);
13064             this.fireEvent("remove", o, key);
13065         }
13066     },
13067    
13068 /**
13069  * Removed an item associated with the passed key fom the collection.
13070  * @param {String} key The key of the item to remove.
13071  */
13072     removeKey : function(key){
13073         return this.removeAt(this.indexOfKey(key));
13074     },
13075    
13076 /**
13077  * Returns the number of items in the collection.
13078  * @return {Number} the number of items in the collection.
13079  */
13080     getCount : function(){
13081         return this.length; 
13082     },
13083    
13084 /**
13085  * Returns index within the collection of the passed Object.
13086  * @param {Object} o The item to find the index of.
13087  * @return {Number} index of the item.
13088  */
13089     indexOf : function(o){
13090         if(!this.items.indexOf){
13091             for(var i = 0, len = this.items.length; i < len; i++){
13092                 if(this.items[i] == o) return i;
13093             }
13094             return -1;
13095         }else{
13096             return this.items.indexOf(o);
13097         }
13098     },
13099    
13100 /**
13101  * Returns index within the collection of the passed key.
13102  * @param {String} key The key to find the index of.
13103  * @return {Number} index of the key.
13104  */
13105     indexOfKey : function(key){
13106         if(!this.keys.indexOf){
13107             for(var i = 0, len = this.keys.length; i < len; i++){
13108                 if(this.keys[i] == key) return i;
13109             }
13110             return -1;
13111         }else{
13112             return this.keys.indexOf(key);
13113         }
13114     },
13115    
13116 /**
13117  * Returns the item associated with the passed key OR index. Key has priority over index.
13118  * @param {String/Number} key The key or index of the item.
13119  * @return {Object} The item associated with the passed key.
13120  */
13121     item : function(key){
13122         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13123         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13124     },
13125     
13126 /**
13127  * Returns the item at the specified index.
13128  * @param {Number} index The index of the item.
13129  * @return {Object}
13130  */
13131     itemAt : function(index){
13132         return this.items[index];
13133     },
13134     
13135 /**
13136  * Returns the item associated with the passed key.
13137  * @param {String/Number} key The key of the item.
13138  * @return {Object} The item associated with the passed key.
13139  */
13140     key : function(key){
13141         return this.map[key];
13142     },
13143    
13144 /**
13145  * Returns true if the collection contains the passed Object as an item.
13146  * @param {Object} o  The Object to look for in the collection.
13147  * @return {Boolean} True if the collection contains the Object as an item.
13148  */
13149     contains : function(o){
13150         return this.indexOf(o) != -1;
13151     },
13152    
13153 /**
13154  * Returns true if the collection contains the passed Object as a key.
13155  * @param {String} key The key to look for in the collection.
13156  * @return {Boolean} True if the collection contains the Object as a key.
13157  */
13158     containsKey : function(key){
13159         return typeof this.map[key] != "undefined";
13160     },
13161    
13162 /**
13163  * Removes all items from the collection.
13164  */
13165     clear : function(){
13166         this.length = 0;
13167         this.items = [];
13168         this.keys = [];
13169         this.map = {};
13170         this.fireEvent("clear");
13171     },
13172    
13173 /**
13174  * Returns the first item in the collection.
13175  * @return {Object} the first item in the collection..
13176  */
13177     first : function(){
13178         return this.items[0]; 
13179     },
13180    
13181 /**
13182  * Returns the last item in the collection.
13183  * @return {Object} the last item in the collection..
13184  */
13185     last : function(){
13186         return this.items[this.length-1];   
13187     },
13188     
13189     _sort : function(property, dir, fn){
13190         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13191         fn = fn || function(a, b){
13192             return a-b;
13193         };
13194         var c = [], k = this.keys, items = this.items;
13195         for(var i = 0, len = items.length; i < len; i++){
13196             c[c.length] = {key: k[i], value: items[i], index: i};
13197         }
13198         c.sort(function(a, b){
13199             var v = fn(a[property], b[property]) * dsc;
13200             if(v == 0){
13201                 v = (a.index < b.index ? -1 : 1);
13202             }
13203             return v;
13204         });
13205         for(var i = 0, len = c.length; i < len; i++){
13206             items[i] = c[i].value;
13207             k[i] = c[i].key;
13208         }
13209         this.fireEvent("sort", this);
13210     },
13211     
13212     /**
13213      * Sorts this collection with the passed comparison function
13214      * @param {String} direction (optional) "ASC" or "DESC"
13215      * @param {Function} fn (optional) comparison function
13216      */
13217     sort : function(dir, fn){
13218         this._sort("value", dir, fn);
13219     },
13220     
13221     /**
13222      * Sorts this collection by keys
13223      * @param {String} direction (optional) "ASC" or "DESC"
13224      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13225      */
13226     keySort : function(dir, fn){
13227         this._sort("key", dir, fn || function(a, b){
13228             return String(a).toUpperCase()-String(b).toUpperCase();
13229         });
13230     },
13231     
13232     /**
13233      * Returns a range of items in this collection
13234      * @param {Number} startIndex (optional) defaults to 0
13235      * @param {Number} endIndex (optional) default to the last item
13236      * @return {Array} An array of items
13237      */
13238     getRange : function(start, end){
13239         var items = this.items;
13240         if(items.length < 1){
13241             return [];
13242         }
13243         start = start || 0;
13244         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13245         var r = [];
13246         if(start <= end){
13247             for(var i = start; i <= end; i++) {
13248                     r[r.length] = items[i];
13249             }
13250         }else{
13251             for(var i = start; i >= end; i--) {
13252                     r[r.length] = items[i];
13253             }
13254         }
13255         return r;
13256     },
13257         
13258     /**
13259      * Filter the <i>objects</i> in this collection by a specific property. 
13260      * Returns a new collection that has been filtered.
13261      * @param {String} property A property on your objects
13262      * @param {String/RegExp} value Either string that the property values 
13263      * should start with or a RegExp to test against the property
13264      * @return {MixedCollection} The new filtered collection
13265      */
13266     filter : function(property, value){
13267         if(!value.exec){ // not a regex
13268             value = String(value);
13269             if(value.length == 0){
13270                 return this.clone();
13271             }
13272             value = new RegExp("^" + Roo.escapeRe(value), "i");
13273         }
13274         return this.filterBy(function(o){
13275             return o && value.test(o[property]);
13276         });
13277         },
13278     
13279     /**
13280      * Filter by a function. * Returns a new collection that has been filtered.
13281      * The passed function will be called with each 
13282      * object in the collection. If the function returns true, the value is included 
13283      * otherwise it is filtered.
13284      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13285      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13286      * @return {MixedCollection} The new filtered collection
13287      */
13288     filterBy : function(fn, scope){
13289         var r = new Roo.util.MixedCollection();
13290         r.getKey = this.getKey;
13291         var k = this.keys, it = this.items;
13292         for(var i = 0, len = it.length; i < len; i++){
13293             if(fn.call(scope||this, it[i], k[i])){
13294                                 r.add(k[i], it[i]);
13295                         }
13296         }
13297         return r;
13298     },
13299     
13300     /**
13301      * Creates a duplicate of this collection
13302      * @return {MixedCollection}
13303      */
13304     clone : function(){
13305         var r = new Roo.util.MixedCollection();
13306         var k = this.keys, it = this.items;
13307         for(var i = 0, len = it.length; i < len; i++){
13308             r.add(k[i], it[i]);
13309         }
13310         r.getKey = this.getKey;
13311         return r;
13312     }
13313 });
13314 /**
13315  * Returns the item associated with the passed key or index.
13316  * @method
13317  * @param {String/Number} key The key or index of the item.
13318  * @return {Object} The item associated with the passed key.
13319  */
13320 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13321  * Based on:
13322  * Ext JS Library 1.1.1
13323  * Copyright(c) 2006-2007, Ext JS, LLC.
13324  *
13325  * Originally Released Under LGPL - original licence link has changed is not relivant.
13326  *
13327  * Fork - LGPL
13328  * <script type="text/javascript">
13329  */
13330 /**
13331  * @class Roo.util.JSON
13332  * Modified version of Douglas Crockford"s json.js that doesn"t
13333  * mess with the Object prototype 
13334  * http://www.json.org/js.html
13335  * @singleton
13336  */
13337 Roo.util.JSON = new (function(){
13338     var useHasOwn = {}.hasOwnProperty ? true : false;
13339     
13340     // crashes Safari in some instances
13341     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13342     
13343     var pad = function(n) {
13344         return n < 10 ? "0" + n : n;
13345     };
13346     
13347     var m = {
13348         "\b": '\\b',
13349         "\t": '\\t',
13350         "\n": '\\n',
13351         "\f": '\\f',
13352         "\r": '\\r',
13353         '"' : '\\"',
13354         "\\": '\\\\'
13355     };
13356
13357     var encodeString = function(s){
13358         if (/["\\\x00-\x1f]/.test(s)) {
13359             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13360                 var c = m[b];
13361                 if(c){
13362                     return c;
13363                 }
13364                 c = b.charCodeAt();
13365                 return "\\u00" +
13366                     Math.floor(c / 16).toString(16) +
13367                     (c % 16).toString(16);
13368             }) + '"';
13369         }
13370         return '"' + s + '"';
13371     };
13372     
13373     var encodeArray = function(o){
13374         var a = ["["], b, i, l = o.length, v;
13375             for (i = 0; i < l; i += 1) {
13376                 v = o[i];
13377                 switch (typeof v) {
13378                     case "undefined":
13379                     case "function":
13380                     case "unknown":
13381                         break;
13382                     default:
13383                         if (b) {
13384                             a.push(',');
13385                         }
13386                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13387                         b = true;
13388                 }
13389             }
13390             a.push("]");
13391             return a.join("");
13392     };
13393     
13394     var encodeDate = function(o){
13395         return '"' + o.getFullYear() + "-" +
13396                 pad(o.getMonth() + 1) + "-" +
13397                 pad(o.getDate()) + "T" +
13398                 pad(o.getHours()) + ":" +
13399                 pad(o.getMinutes()) + ":" +
13400                 pad(o.getSeconds()) + '"';
13401     };
13402     
13403     /**
13404      * Encodes an Object, Array or other value
13405      * @param {Mixed} o The variable to encode
13406      * @return {String} The JSON string
13407      */
13408     this.encode = function(o)
13409     {
13410         // should this be extended to fully wrap stringify..
13411         
13412         if(typeof o == "undefined" || o === null){
13413             return "null";
13414         }else if(o instanceof Array){
13415             return encodeArray(o);
13416         }else if(o instanceof Date){
13417             return encodeDate(o);
13418         }else if(typeof o == "string"){
13419             return encodeString(o);
13420         }else if(typeof o == "number"){
13421             return isFinite(o) ? String(o) : "null";
13422         }else if(typeof o == "boolean"){
13423             return String(o);
13424         }else {
13425             var a = ["{"], b, i, v;
13426             for (i in o) {
13427                 if(!useHasOwn || o.hasOwnProperty(i)) {
13428                     v = o[i];
13429                     switch (typeof v) {
13430                     case "undefined":
13431                     case "function":
13432                     case "unknown":
13433                         break;
13434                     default:
13435                         if(b){
13436                             a.push(',');
13437                         }
13438                         a.push(this.encode(i), ":",
13439                                 v === null ? "null" : this.encode(v));
13440                         b = true;
13441                     }
13442                 }
13443             }
13444             a.push("}");
13445             return a.join("");
13446         }
13447     };
13448     
13449     /**
13450      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13451      * @param {String} json The JSON string
13452      * @return {Object} The resulting object
13453      */
13454     this.decode = function(json){
13455         
13456         return  /** eval:var:json */ eval("(" + json + ')');
13457     };
13458 })();
13459 /** 
13460  * Shorthand for {@link Roo.util.JSON#encode}
13461  * @member Roo encode 
13462  * @method */
13463 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13464 /** 
13465  * Shorthand for {@link Roo.util.JSON#decode}
13466  * @member Roo decode 
13467  * @method */
13468 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13469 /*
13470  * Based on:
13471  * Ext JS Library 1.1.1
13472  * Copyright(c) 2006-2007, Ext JS, LLC.
13473  *
13474  * Originally Released Under LGPL - original licence link has changed is not relivant.
13475  *
13476  * Fork - LGPL
13477  * <script type="text/javascript">
13478  */
13479  
13480 /**
13481  * @class Roo.util.Format
13482  * Reusable data formatting functions
13483  * @singleton
13484  */
13485 Roo.util.Format = function(){
13486     var trimRe = /^\s+|\s+$/g;
13487     return {
13488         /**
13489          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13490          * @param {String} value The string to truncate
13491          * @param {Number} length The maximum length to allow before truncating
13492          * @return {String} The converted text
13493          */
13494         ellipsis : function(value, len){
13495             if(value && value.length > len){
13496                 return value.substr(0, len-3)+"...";
13497             }
13498             return value;
13499         },
13500
13501         /**
13502          * Checks a reference and converts it to empty string if it is undefined
13503          * @param {Mixed} value Reference to check
13504          * @return {Mixed} Empty string if converted, otherwise the original value
13505          */
13506         undef : function(value){
13507             return typeof value != "undefined" ? value : "";
13508         },
13509
13510         /**
13511          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13512          * @param {String} value The string to encode
13513          * @return {String} The encoded text
13514          */
13515         htmlEncode : function(value){
13516             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13517         },
13518
13519         /**
13520          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13521          * @param {String} value The string to decode
13522          * @return {String} The decoded text
13523          */
13524         htmlDecode : function(value){
13525             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13526         },
13527
13528         /**
13529          * Trims any whitespace from either side of a string
13530          * @param {String} value The text to trim
13531          * @return {String} The trimmed text
13532          */
13533         trim : function(value){
13534             return String(value).replace(trimRe, "");
13535         },
13536
13537         /**
13538          * Returns a substring from within an original string
13539          * @param {String} value The original text
13540          * @param {Number} start The start index of the substring
13541          * @param {Number} length The length of the substring
13542          * @return {String} The substring
13543          */
13544         substr : function(value, start, length){
13545             return String(value).substr(start, length);
13546         },
13547
13548         /**
13549          * Converts a string to all lower case letters
13550          * @param {String} value The text to convert
13551          * @return {String} The converted text
13552          */
13553         lowercase : function(value){
13554             return String(value).toLowerCase();
13555         },
13556
13557         /**
13558          * Converts a string to all upper case letters
13559          * @param {String} value The text to convert
13560          * @return {String} The converted text
13561          */
13562         uppercase : function(value){
13563             return String(value).toUpperCase();
13564         },
13565
13566         /**
13567          * Converts the first character only of a string to upper case
13568          * @param {String} value The text to convert
13569          * @return {String} The converted text
13570          */
13571         capitalize : function(value){
13572             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13573         },
13574
13575         // private
13576         call : function(value, fn){
13577             if(arguments.length > 2){
13578                 var args = Array.prototype.slice.call(arguments, 2);
13579                 args.unshift(value);
13580                  
13581                 return /** eval:var:value */  eval(fn).apply(window, args);
13582             }else{
13583                 /** eval:var:value */
13584                 return /** eval:var:value */ eval(fn).call(window, value);
13585             }
13586         },
13587
13588        
13589         /**
13590          * safer version of Math.toFixed..??/
13591          * @param {Number/String} value The numeric value to format
13592          * @param {Number/String} value Decimal places 
13593          * @return {String} The formatted currency string
13594          */
13595         toFixed : function(v, n)
13596         {
13597             // why not use to fixed - precision is buggered???
13598             if (!n) {
13599                 return Math.round(v-0);
13600             }
13601             var fact = Math.pow(10,n+1);
13602             v = (Math.round((v-0)*fact))/fact;
13603             var z = (''+fact).substring(2);
13604             if (v == Math.floor(v)) {
13605                 return Math.floor(v) + '.' + z;
13606             }
13607             
13608             // now just padd decimals..
13609             var ps = String(v).split('.');
13610             var fd = (ps[1] + z);
13611             var r = fd.substring(0,n); 
13612             var rm = fd.substring(n); 
13613             if (rm < 5) {
13614                 return ps[0] + '.' + r;
13615             }
13616             r*=1; // turn it into a number;
13617             r++;
13618             if (String(r).length != n) {
13619                 ps[0]*=1;
13620                 ps[0]++;
13621                 r = String(r).substring(1); // chop the end off.
13622             }
13623             
13624             return ps[0] + '.' + r;
13625              
13626         },
13627         
13628         /**
13629          * Format a number as US currency
13630          * @param {Number/String} value The numeric value to format
13631          * @return {String} The formatted currency string
13632          */
13633         usMoney : function(v){
13634             return '$' + Roo.util.Format.number(v);
13635         },
13636         
13637         /**
13638          * Format a number
13639          * eventually this should probably emulate php's number_format
13640          * @param {Number/String} value The numeric value to format
13641          * @param {Number} decimals number of decimal places
13642          * @return {String} The formatted currency string
13643          */
13644         number : function(v,decimals)
13645         {
13646             // multiply and round.
13647             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13648             var mul = Math.pow(10, decimals);
13649             var zero = String(mul).substring(1);
13650             v = (Math.round((v-0)*mul))/mul;
13651             
13652             // if it's '0' number.. then
13653             
13654             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13655             v = String(v);
13656             var ps = v.split('.');
13657             var whole = ps[0];
13658             
13659             
13660             var r = /(\d+)(\d{3})/;
13661             // add comma's
13662             while (r.test(whole)) {
13663                 whole = whole.replace(r, '$1' + ',' + '$2');
13664             }
13665             
13666             
13667             var sub = ps[1] ?
13668                     // has decimals..
13669                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13670                     // does not have decimals
13671                     (decimals ? ('.' + zero) : '');
13672             
13673             
13674             return whole + sub ;
13675         },
13676         
13677         /**
13678          * Parse a value into a formatted date using the specified format pattern.
13679          * @param {Mixed} value The value to format
13680          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13681          * @return {String} The formatted date string
13682          */
13683         date : function(v, format){
13684             if(!v){
13685                 return "";
13686             }
13687             if(!(v instanceof Date)){
13688                 v = new Date(Date.parse(v));
13689             }
13690             return v.dateFormat(format || Roo.util.Format.defaults.date);
13691         },
13692
13693         /**
13694          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13695          * @param {String} format Any valid date format string
13696          * @return {Function} The date formatting function
13697          */
13698         dateRenderer : function(format){
13699             return function(v){
13700                 return Roo.util.Format.date(v, format);  
13701             };
13702         },
13703
13704         // private
13705         stripTagsRE : /<\/?[^>]+>/gi,
13706         
13707         /**
13708          * Strips all HTML tags
13709          * @param {Mixed} value The text from which to strip tags
13710          * @return {String} The stripped text
13711          */
13712         stripTags : function(v){
13713             return !v ? v : String(v).replace(this.stripTagsRE, "");
13714         }
13715     };
13716 }();
13717 Roo.util.Format.defaults = {
13718     date : 'd/M/Y'
13719 };/*
13720  * Based on:
13721  * Ext JS Library 1.1.1
13722  * Copyright(c) 2006-2007, Ext JS, LLC.
13723  *
13724  * Originally Released Under LGPL - original licence link has changed is not relivant.
13725  *
13726  * Fork - LGPL
13727  * <script type="text/javascript">
13728  */
13729
13730
13731  
13732
13733 /**
13734  * @class Roo.MasterTemplate
13735  * @extends Roo.Template
13736  * Provides a template that can have child templates. The syntax is:
13737 <pre><code>
13738 var t = new Roo.MasterTemplate(
13739         '&lt;select name="{name}"&gt;',
13740                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13741         '&lt;/select&gt;'
13742 );
13743 t.add('options', {value: 'foo', text: 'bar'});
13744 // or you can add multiple child elements in one shot
13745 t.addAll('options', [
13746     {value: 'foo', text: 'bar'},
13747     {value: 'foo2', text: 'bar2'},
13748     {value: 'foo3', text: 'bar3'}
13749 ]);
13750 // then append, applying the master template values
13751 t.append('my-form', {name: 'my-select'});
13752 </code></pre>
13753 * A name attribute for the child template is not required if you have only one child
13754 * template or you want to refer to them by index.
13755  */
13756 Roo.MasterTemplate = function(){
13757     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13758     this.originalHtml = this.html;
13759     var st = {};
13760     var m, re = this.subTemplateRe;
13761     re.lastIndex = 0;
13762     var subIndex = 0;
13763     while(m = re.exec(this.html)){
13764         var name = m[1], content = m[2];
13765         st[subIndex] = {
13766             name: name,
13767             index: subIndex,
13768             buffer: [],
13769             tpl : new Roo.Template(content)
13770         };
13771         if(name){
13772             st[name] = st[subIndex];
13773         }
13774         st[subIndex].tpl.compile();
13775         st[subIndex].tpl.call = this.call.createDelegate(this);
13776         subIndex++;
13777     }
13778     this.subCount = subIndex;
13779     this.subs = st;
13780 };
13781 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13782     /**
13783     * The regular expression used to match sub templates
13784     * @type RegExp
13785     * @property
13786     */
13787     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13788
13789     /**
13790      * Applies the passed values to a child template.
13791      * @param {String/Number} name (optional) The name or index of the child template
13792      * @param {Array/Object} values The values to be applied to the template
13793      * @return {MasterTemplate} this
13794      */
13795      add : function(name, values){
13796         if(arguments.length == 1){
13797             values = arguments[0];
13798             name = 0;
13799         }
13800         var s = this.subs[name];
13801         s.buffer[s.buffer.length] = s.tpl.apply(values);
13802         return this;
13803     },
13804
13805     /**
13806      * Applies all the passed values to a child template.
13807      * @param {String/Number} name (optional) The name or index of the child template
13808      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13809      * @param {Boolean} reset (optional) True to reset the template first
13810      * @return {MasterTemplate} this
13811      */
13812     fill : function(name, values, reset){
13813         var a = arguments;
13814         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13815             values = a[0];
13816             name = 0;
13817             reset = a[1];
13818         }
13819         if(reset){
13820             this.reset();
13821         }
13822         for(var i = 0, len = values.length; i < len; i++){
13823             this.add(name, values[i]);
13824         }
13825         return this;
13826     },
13827
13828     /**
13829      * Resets the template for reuse
13830      * @return {MasterTemplate} this
13831      */
13832      reset : function(){
13833         var s = this.subs;
13834         for(var i = 0; i < this.subCount; i++){
13835             s[i].buffer = [];
13836         }
13837         return this;
13838     },
13839
13840     applyTemplate : function(values){
13841         var s = this.subs;
13842         var replaceIndex = -1;
13843         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13844             return s[++replaceIndex].buffer.join("");
13845         });
13846         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13847     },
13848
13849     apply : function(){
13850         return this.applyTemplate.apply(this, arguments);
13851     },
13852
13853     compile : function(){return this;}
13854 });
13855
13856 /**
13857  * Alias for fill().
13858  * @method
13859  */
13860 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13861  /**
13862  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13863  * var tpl = Roo.MasterTemplate.from('element-id');
13864  * @param {String/HTMLElement} el
13865  * @param {Object} config
13866  * @static
13867  */
13868 Roo.MasterTemplate.from = function(el, config){
13869     el = Roo.getDom(el);
13870     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13871 };/*
13872  * Based on:
13873  * Ext JS Library 1.1.1
13874  * Copyright(c) 2006-2007, Ext JS, LLC.
13875  *
13876  * Originally Released Under LGPL - original licence link has changed is not relivant.
13877  *
13878  * Fork - LGPL
13879  * <script type="text/javascript">
13880  */
13881
13882  
13883 /**
13884  * @class Roo.util.CSS
13885  * Utility class for manipulating CSS rules
13886  * @singleton
13887  */
13888 Roo.util.CSS = function(){
13889         var rules = null;
13890         var doc = document;
13891
13892     var camelRe = /(-[a-z])/gi;
13893     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13894
13895    return {
13896    /**
13897     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13898     * tag and appended to the HEAD of the document.
13899     * @param {String|Object} cssText The text containing the css rules
13900     * @param {String} id An id to add to the stylesheet for later removal
13901     * @return {StyleSheet}
13902     */
13903     createStyleSheet : function(cssText, id){
13904         var ss;
13905         var head = doc.getElementsByTagName("head")[0];
13906         var nrules = doc.createElement("style");
13907         nrules.setAttribute("type", "text/css");
13908         if(id){
13909             nrules.setAttribute("id", id);
13910         }
13911         if (typeof(cssText) != 'string') {
13912             // support object maps..
13913             // not sure if this a good idea.. 
13914             // perhaps it should be merged with the general css handling
13915             // and handle js style props.
13916             var cssTextNew = [];
13917             for(var n in cssText) {
13918                 var citems = [];
13919                 for(var k in cssText[n]) {
13920                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13921                 }
13922                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13923                 
13924             }
13925             cssText = cssTextNew.join("\n");
13926             
13927         }
13928        
13929        
13930        if(Roo.isIE){
13931            head.appendChild(nrules);
13932            ss = nrules.styleSheet;
13933            ss.cssText = cssText;
13934        }else{
13935            try{
13936                 nrules.appendChild(doc.createTextNode(cssText));
13937            }catch(e){
13938                nrules.cssText = cssText; 
13939            }
13940            head.appendChild(nrules);
13941            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13942        }
13943        this.cacheStyleSheet(ss);
13944        return ss;
13945    },
13946
13947    /**
13948     * Removes a style or link tag by id
13949     * @param {String} id The id of the tag
13950     */
13951    removeStyleSheet : function(id){
13952        var existing = doc.getElementById(id);
13953        if(existing){
13954            existing.parentNode.removeChild(existing);
13955        }
13956    },
13957
13958    /**
13959     * Dynamically swaps an existing stylesheet reference for a new one
13960     * @param {String} id The id of an existing link tag to remove
13961     * @param {String} url The href of the new stylesheet to include
13962     */
13963    swapStyleSheet : function(id, url){
13964        this.removeStyleSheet(id);
13965        var ss = doc.createElement("link");
13966        ss.setAttribute("rel", "stylesheet");
13967        ss.setAttribute("type", "text/css");
13968        ss.setAttribute("id", id);
13969        ss.setAttribute("href", url);
13970        doc.getElementsByTagName("head")[0].appendChild(ss);
13971    },
13972    
13973    /**
13974     * Refresh the rule cache if you have dynamically added stylesheets
13975     * @return {Object} An object (hash) of rules indexed by selector
13976     */
13977    refreshCache : function(){
13978        return this.getRules(true);
13979    },
13980
13981    // private
13982    cacheStyleSheet : function(stylesheet){
13983        if(!rules){
13984            rules = {};
13985        }
13986        try{// try catch for cross domain access issue
13987            var ssRules = stylesheet.cssRules || stylesheet.rules;
13988            for(var j = ssRules.length-1; j >= 0; --j){
13989                rules[ssRules[j].selectorText] = ssRules[j];
13990            }
13991        }catch(e){}
13992    },
13993    
13994    /**
13995     * Gets all css rules for the document
13996     * @param {Boolean} refreshCache true to refresh the internal cache
13997     * @return {Object} An object (hash) of rules indexed by selector
13998     */
13999    getRules : function(refreshCache){
14000                 if(rules == null || refreshCache){
14001                         rules = {};
14002                         var ds = doc.styleSheets;
14003                         for(var i =0, len = ds.length; i < len; i++){
14004                             try{
14005                         this.cacheStyleSheet(ds[i]);
14006                     }catch(e){} 
14007                 }
14008                 }
14009                 return rules;
14010         },
14011         
14012         /**
14013     * Gets an an individual CSS rule by selector(s)
14014     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14015     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14016     * @return {CSSRule} The CSS rule or null if one is not found
14017     */
14018    getRule : function(selector, refreshCache){
14019                 var rs = this.getRules(refreshCache);
14020                 if(!(selector instanceof Array)){
14021                     return rs[selector];
14022                 }
14023                 for(var i = 0; i < selector.length; i++){
14024                         if(rs[selector[i]]){
14025                                 return rs[selector[i]];
14026                         }
14027                 }
14028                 return null;
14029         },
14030         
14031         
14032         /**
14033     * Updates a rule property
14034     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14035     * @param {String} property The css property
14036     * @param {String} value The new value for the property
14037     * @return {Boolean} true If a rule was found and updated
14038     */
14039    updateRule : function(selector, property, value){
14040                 if(!(selector instanceof Array)){
14041                         var rule = this.getRule(selector);
14042                         if(rule){
14043                                 rule.style[property.replace(camelRe, camelFn)] = value;
14044                                 return true;
14045                         }
14046                 }else{
14047                         for(var i = 0; i < selector.length; i++){
14048                                 if(this.updateRule(selector[i], property, value)){
14049                                         return true;
14050                                 }
14051                         }
14052                 }
14053                 return false;
14054         }
14055    };   
14056 }();/*
14057  * Based on:
14058  * Ext JS Library 1.1.1
14059  * Copyright(c) 2006-2007, Ext JS, LLC.
14060  *
14061  * Originally Released Under LGPL - original licence link has changed is not relivant.
14062  *
14063  * Fork - LGPL
14064  * <script type="text/javascript">
14065  */
14066
14067  
14068
14069 /**
14070  * @class Roo.util.ClickRepeater
14071  * @extends Roo.util.Observable
14072  * 
14073  * A wrapper class which can be applied to any element. Fires a "click" event while the
14074  * mouse is pressed. The interval between firings may be specified in the config but
14075  * defaults to 10 milliseconds.
14076  * 
14077  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14078  * 
14079  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14080  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14081  * Similar to an autorepeat key delay.
14082  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14083  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14084  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14085  *           "interval" and "delay" are ignored. "immediate" is honored.
14086  * @cfg {Boolean} preventDefault True to prevent the default click event
14087  * @cfg {Boolean} stopDefault True to stop the default click event
14088  * 
14089  * @history
14090  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14091  *     2007-02-02 jvs Renamed to ClickRepeater
14092  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14093  *
14094  *  @constructor
14095  * @param {String/HTMLElement/Element} el The element to listen on
14096  * @param {Object} config
14097  **/
14098 Roo.util.ClickRepeater = function(el, config)
14099 {
14100     this.el = Roo.get(el);
14101     this.el.unselectable();
14102
14103     Roo.apply(this, config);
14104
14105     this.addEvents({
14106     /**
14107      * @event mousedown
14108      * Fires when the mouse button is depressed.
14109      * @param {Roo.util.ClickRepeater} this
14110      */
14111         "mousedown" : true,
14112     /**
14113      * @event click
14114      * Fires on a specified interval during the time the element is pressed.
14115      * @param {Roo.util.ClickRepeater} this
14116      */
14117         "click" : true,
14118     /**
14119      * @event mouseup
14120      * Fires when the mouse key is released.
14121      * @param {Roo.util.ClickRepeater} this
14122      */
14123         "mouseup" : true
14124     });
14125
14126     this.el.on("mousedown", this.handleMouseDown, this);
14127     if(this.preventDefault || this.stopDefault){
14128         this.el.on("click", function(e){
14129             if(this.preventDefault){
14130                 e.preventDefault();
14131             }
14132             if(this.stopDefault){
14133                 e.stopEvent();
14134             }
14135         }, this);
14136     }
14137
14138     // allow inline handler
14139     if(this.handler){
14140         this.on("click", this.handler,  this.scope || this);
14141     }
14142
14143     Roo.util.ClickRepeater.superclass.constructor.call(this);
14144 };
14145
14146 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14147     interval : 20,
14148     delay: 250,
14149     preventDefault : true,
14150     stopDefault : false,
14151     timer : 0,
14152
14153     // private
14154     handleMouseDown : function(){
14155         clearTimeout(this.timer);
14156         this.el.blur();
14157         if(this.pressClass){
14158             this.el.addClass(this.pressClass);
14159         }
14160         this.mousedownTime = new Date();
14161
14162         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14163         this.el.on("mouseout", this.handleMouseOut, this);
14164
14165         this.fireEvent("mousedown", this);
14166         this.fireEvent("click", this);
14167         
14168         this.timer = this.click.defer(this.delay || this.interval, this);
14169     },
14170
14171     // private
14172     click : function(){
14173         this.fireEvent("click", this);
14174         this.timer = this.click.defer(this.getInterval(), this);
14175     },
14176
14177     // private
14178     getInterval: function(){
14179         if(!this.accelerate){
14180             return this.interval;
14181         }
14182         var pressTime = this.mousedownTime.getElapsed();
14183         if(pressTime < 500){
14184             return 400;
14185         }else if(pressTime < 1700){
14186             return 320;
14187         }else if(pressTime < 2600){
14188             return 250;
14189         }else if(pressTime < 3500){
14190             return 180;
14191         }else if(pressTime < 4400){
14192             return 140;
14193         }else if(pressTime < 5300){
14194             return 80;
14195         }else if(pressTime < 6200){
14196             return 50;
14197         }else{
14198             return 10;
14199         }
14200     },
14201
14202     // private
14203     handleMouseOut : function(){
14204         clearTimeout(this.timer);
14205         if(this.pressClass){
14206             this.el.removeClass(this.pressClass);
14207         }
14208         this.el.on("mouseover", this.handleMouseReturn, this);
14209     },
14210
14211     // private
14212     handleMouseReturn : function(){
14213         this.el.un("mouseover", this.handleMouseReturn);
14214         if(this.pressClass){
14215             this.el.addClass(this.pressClass);
14216         }
14217         this.click();
14218     },
14219
14220     // private
14221     handleMouseUp : function(){
14222         clearTimeout(this.timer);
14223         this.el.un("mouseover", this.handleMouseReturn);
14224         this.el.un("mouseout", this.handleMouseOut);
14225         Roo.get(document).un("mouseup", this.handleMouseUp);
14226         this.el.removeClass(this.pressClass);
14227         this.fireEvent("mouseup", this);
14228     }
14229 });/*
14230  * Based on:
14231  * Ext JS Library 1.1.1
14232  * Copyright(c) 2006-2007, Ext JS, LLC.
14233  *
14234  * Originally Released Under LGPL - original licence link has changed is not relivant.
14235  *
14236  * Fork - LGPL
14237  * <script type="text/javascript">
14238  */
14239
14240  
14241 /**
14242  * @class Roo.KeyNav
14243  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14244  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14245  * way to implement custom navigation schemes for any UI component.</p>
14246  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14247  * pageUp, pageDown, del, home, end.  Usage:</p>
14248  <pre><code>
14249 var nav = new Roo.KeyNav("my-element", {
14250     "left" : function(e){
14251         this.moveLeft(e.ctrlKey);
14252     },
14253     "right" : function(e){
14254         this.moveRight(e.ctrlKey);
14255     },
14256     "enter" : function(e){
14257         this.save();
14258     },
14259     scope : this
14260 });
14261 </code></pre>
14262  * @constructor
14263  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14264  * @param {Object} config The config
14265  */
14266 Roo.KeyNav = function(el, config){
14267     this.el = Roo.get(el);
14268     Roo.apply(this, config);
14269     if(!this.disabled){
14270         this.disabled = true;
14271         this.enable();
14272     }
14273 };
14274
14275 Roo.KeyNav.prototype = {
14276     /**
14277      * @cfg {Boolean} disabled
14278      * True to disable this KeyNav instance (defaults to false)
14279      */
14280     disabled : false,
14281     /**
14282      * @cfg {String} defaultEventAction
14283      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14284      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14285      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14286      */
14287     defaultEventAction: "stopEvent",
14288     /**
14289      * @cfg {Boolean} forceKeyDown
14290      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14291      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14292      * handle keydown instead of keypress.
14293      */
14294     forceKeyDown : false,
14295
14296     // private
14297     prepareEvent : function(e){
14298         var k = e.getKey();
14299         var h = this.keyToHandler[k];
14300         //if(h && this[h]){
14301         //    e.stopPropagation();
14302         //}
14303         if(Roo.isSafari && h && k >= 37 && k <= 40){
14304             e.stopEvent();
14305         }
14306     },
14307
14308     // private
14309     relay : function(e){
14310         var k = e.getKey();
14311         var h = this.keyToHandler[k];
14312         if(h && this[h]){
14313             if(this.doRelay(e, this[h], h) !== true){
14314                 e[this.defaultEventAction]();
14315             }
14316         }
14317     },
14318
14319     // private
14320     doRelay : function(e, h, hname){
14321         return h.call(this.scope || this, e);
14322     },
14323
14324     // possible handlers
14325     enter : false,
14326     left : false,
14327     right : false,
14328     up : false,
14329     down : false,
14330     tab : false,
14331     esc : false,
14332     pageUp : false,
14333     pageDown : false,
14334     del : false,
14335     home : false,
14336     end : false,
14337
14338     // quick lookup hash
14339     keyToHandler : {
14340         37 : "left",
14341         39 : "right",
14342         38 : "up",
14343         40 : "down",
14344         33 : "pageUp",
14345         34 : "pageDown",
14346         46 : "del",
14347         36 : "home",
14348         35 : "end",
14349         13 : "enter",
14350         27 : "esc",
14351         9  : "tab"
14352     },
14353
14354         /**
14355          * Enable this KeyNav
14356          */
14357         enable: function(){
14358                 if(this.disabled){
14359             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14360             // the EventObject will normalize Safari automatically
14361             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14362                 this.el.on("keydown", this.relay,  this);
14363             }else{
14364                 this.el.on("keydown", this.prepareEvent,  this);
14365                 this.el.on("keypress", this.relay,  this);
14366             }
14367                     this.disabled = false;
14368                 }
14369         },
14370
14371         /**
14372          * Disable this KeyNav
14373          */
14374         disable: function(){
14375                 if(!this.disabled){
14376                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14377                 this.el.un("keydown", this.relay);
14378             }else{
14379                 this.el.un("keydown", this.prepareEvent);
14380                 this.el.un("keypress", this.relay);
14381             }
14382                     this.disabled = true;
14383                 }
14384         }
14385 };/*
14386  * Based on:
14387  * Ext JS Library 1.1.1
14388  * Copyright(c) 2006-2007, Ext JS, LLC.
14389  *
14390  * Originally Released Under LGPL - original licence link has changed is not relivant.
14391  *
14392  * Fork - LGPL
14393  * <script type="text/javascript">
14394  */
14395
14396  
14397 /**
14398  * @class Roo.KeyMap
14399  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14400  * The constructor accepts the same config object as defined by {@link #addBinding}.
14401  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14402  * combination it will call the function with this signature (if the match is a multi-key
14403  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14404  * A KeyMap can also handle a string representation of keys.<br />
14405  * Usage:
14406  <pre><code>
14407 // map one key by key code
14408 var map = new Roo.KeyMap("my-element", {
14409     key: 13, // or Roo.EventObject.ENTER
14410     fn: myHandler,
14411     scope: myObject
14412 });
14413
14414 // map multiple keys to one action by string
14415 var map = new Roo.KeyMap("my-element", {
14416     key: "a\r\n\t",
14417     fn: myHandler,
14418     scope: myObject
14419 });
14420
14421 // map multiple keys to multiple actions by strings and array of codes
14422 var map = new Roo.KeyMap("my-element", [
14423     {
14424         key: [10,13],
14425         fn: function(){ alert("Return was pressed"); }
14426     }, {
14427         key: "abc",
14428         fn: function(){ alert('a, b or c was pressed'); }
14429     }, {
14430         key: "\t",
14431         ctrl:true,
14432         shift:true,
14433         fn: function(){ alert('Control + shift + tab was pressed.'); }
14434     }
14435 ]);
14436 </code></pre>
14437  * <b>Note: A KeyMap starts enabled</b>
14438  * @constructor
14439  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14440  * @param {Object} config The config (see {@link #addBinding})
14441  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14442  */
14443 Roo.KeyMap = function(el, config, eventName){
14444     this.el  = Roo.get(el);
14445     this.eventName = eventName || "keydown";
14446     this.bindings = [];
14447     if(config){
14448         this.addBinding(config);
14449     }
14450     this.enable();
14451 };
14452
14453 Roo.KeyMap.prototype = {
14454     /**
14455      * True to stop the event from bubbling and prevent the default browser action if the
14456      * key was handled by the KeyMap (defaults to false)
14457      * @type Boolean
14458      */
14459     stopEvent : false,
14460
14461     /**
14462      * Add a new binding to this KeyMap. The following config object properties are supported:
14463      * <pre>
14464 Property    Type             Description
14465 ----------  ---------------  ----------------------------------------------------------------------
14466 key         String/Array     A single keycode or an array of keycodes to handle
14467 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14468 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14469 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14470 fn          Function         The function to call when KeyMap finds the expected key combination
14471 scope       Object           The scope of the callback function
14472 </pre>
14473      *
14474      * Usage:
14475      * <pre><code>
14476 // Create a KeyMap
14477 var map = new Roo.KeyMap(document, {
14478     key: Roo.EventObject.ENTER,
14479     fn: handleKey,
14480     scope: this
14481 });
14482
14483 //Add a new binding to the existing KeyMap later
14484 map.addBinding({
14485     key: 'abc',
14486     shift: true,
14487     fn: handleKey,
14488     scope: this
14489 });
14490 </code></pre>
14491      * @param {Object/Array} config A single KeyMap config or an array of configs
14492      */
14493         addBinding : function(config){
14494         if(config instanceof Array){
14495             for(var i = 0, len = config.length; i < len; i++){
14496                 this.addBinding(config[i]);
14497             }
14498             return;
14499         }
14500         var keyCode = config.key,
14501             shift = config.shift, 
14502             ctrl = config.ctrl, 
14503             alt = config.alt,
14504             fn = config.fn,
14505             scope = config.scope;
14506         if(typeof keyCode == "string"){
14507             var ks = [];
14508             var keyString = keyCode.toUpperCase();
14509             for(var j = 0, len = keyString.length; j < len; j++){
14510                 ks.push(keyString.charCodeAt(j));
14511             }
14512             keyCode = ks;
14513         }
14514         var keyArray = keyCode instanceof Array;
14515         var handler = function(e){
14516             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14517                 var k = e.getKey();
14518                 if(keyArray){
14519                     for(var i = 0, len = keyCode.length; i < len; i++){
14520                         if(keyCode[i] == k){
14521                           if(this.stopEvent){
14522                               e.stopEvent();
14523                           }
14524                           fn.call(scope || window, k, e);
14525                           return;
14526                         }
14527                     }
14528                 }else{
14529                     if(k == keyCode){
14530                         if(this.stopEvent){
14531                            e.stopEvent();
14532                         }
14533                         fn.call(scope || window, k, e);
14534                     }
14535                 }
14536             }
14537         };
14538         this.bindings.push(handler);  
14539         },
14540
14541     /**
14542      * Shorthand for adding a single key listener
14543      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14544      * following options:
14545      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14546      * @param {Function} fn The function to call
14547      * @param {Object} scope (optional) The scope of the function
14548      */
14549     on : function(key, fn, scope){
14550         var keyCode, shift, ctrl, alt;
14551         if(typeof key == "object" && !(key instanceof Array)){
14552             keyCode = key.key;
14553             shift = key.shift;
14554             ctrl = key.ctrl;
14555             alt = key.alt;
14556         }else{
14557             keyCode = key;
14558         }
14559         this.addBinding({
14560             key: keyCode,
14561             shift: shift,
14562             ctrl: ctrl,
14563             alt: alt,
14564             fn: fn,
14565             scope: scope
14566         })
14567     },
14568
14569     // private
14570     handleKeyDown : function(e){
14571             if(this.enabled){ //just in case
14572             var b = this.bindings;
14573             for(var i = 0, len = b.length; i < len; i++){
14574                 b[i].call(this, e);
14575             }
14576             }
14577         },
14578         
14579         /**
14580          * Returns true if this KeyMap is enabled
14581          * @return {Boolean} 
14582          */
14583         isEnabled : function(){
14584             return this.enabled;  
14585         },
14586         
14587         /**
14588          * Enables this KeyMap
14589          */
14590         enable: function(){
14591                 if(!this.enabled){
14592                     this.el.on(this.eventName, this.handleKeyDown, this);
14593                     this.enabled = true;
14594                 }
14595         },
14596
14597         /**
14598          * Disable this KeyMap
14599          */
14600         disable: function(){
14601                 if(this.enabled){
14602                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14603                     this.enabled = false;
14604                 }
14605         }
14606 };/*
14607  * Based on:
14608  * Ext JS Library 1.1.1
14609  * Copyright(c) 2006-2007, Ext JS, LLC.
14610  *
14611  * Originally Released Under LGPL - original licence link has changed is not relivant.
14612  *
14613  * Fork - LGPL
14614  * <script type="text/javascript">
14615  */
14616
14617  
14618 /**
14619  * @class Roo.util.TextMetrics
14620  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14621  * wide, in pixels, a given block of text will be.
14622  * @singleton
14623  */
14624 Roo.util.TextMetrics = function(){
14625     var shared;
14626     return {
14627         /**
14628          * Measures the size of the specified text
14629          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14630          * that can affect the size of the rendered text
14631          * @param {String} text The text to measure
14632          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14633          * in order to accurately measure the text height
14634          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14635          */
14636         measure : function(el, text, fixedWidth){
14637             if(!shared){
14638                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14639             }
14640             shared.bind(el);
14641             shared.setFixedWidth(fixedWidth || 'auto');
14642             return shared.getSize(text);
14643         },
14644
14645         /**
14646          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14647          * the overhead of multiple calls to initialize the style properties on each measurement.
14648          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14649          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14650          * in order to accurately measure the text height
14651          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14652          */
14653         createInstance : function(el, fixedWidth){
14654             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14655         }
14656     };
14657 }();
14658
14659  
14660
14661 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14662     var ml = new Roo.Element(document.createElement('div'));
14663     document.body.appendChild(ml.dom);
14664     ml.position('absolute');
14665     ml.setLeftTop(-1000, -1000);
14666     ml.hide();
14667
14668     if(fixedWidth){
14669         ml.setWidth(fixedWidth);
14670     }
14671      
14672     var instance = {
14673         /**
14674          * Returns the size of the specified text based on the internal element's style and width properties
14675          * @memberOf Roo.util.TextMetrics.Instance#
14676          * @param {String} text The text to measure
14677          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14678          */
14679         getSize : function(text){
14680             ml.update(text);
14681             var s = ml.getSize();
14682             ml.update('');
14683             return s;
14684         },
14685
14686         /**
14687          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14688          * that can affect the size of the rendered text
14689          * @memberOf Roo.util.TextMetrics.Instance#
14690          * @param {String/HTMLElement} el The element, dom node or id
14691          */
14692         bind : function(el){
14693             ml.setStyle(
14694                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14695             );
14696         },
14697
14698         /**
14699          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14700          * to set a fixed width in order to accurately measure the text height.
14701          * @memberOf Roo.util.TextMetrics.Instance#
14702          * @param {Number} width The width to set on the element
14703          */
14704         setFixedWidth : function(width){
14705             ml.setWidth(width);
14706         },
14707
14708         /**
14709          * Returns the measured width of the specified text
14710          * @memberOf Roo.util.TextMetrics.Instance#
14711          * @param {String} text The text to measure
14712          * @return {Number} width The width in pixels
14713          */
14714         getWidth : function(text){
14715             ml.dom.style.width = 'auto';
14716             return this.getSize(text).width;
14717         },
14718
14719         /**
14720          * Returns the measured height of the specified text.  For multiline text, be sure to call
14721          * {@link #setFixedWidth} if necessary.
14722          * @memberOf Roo.util.TextMetrics.Instance#
14723          * @param {String} text The text to measure
14724          * @return {Number} height The height in pixels
14725          */
14726         getHeight : function(text){
14727             return this.getSize(text).height;
14728         }
14729     };
14730
14731     instance.bind(bindTo);
14732
14733     return instance;
14734 };
14735
14736 // backwards compat
14737 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14738  * Based on:
14739  * Ext JS Library 1.1.1
14740  * Copyright(c) 2006-2007, Ext JS, LLC.
14741  *
14742  * Originally Released Under LGPL - original licence link has changed is not relivant.
14743  *
14744  * Fork - LGPL
14745  * <script type="text/javascript">
14746  */
14747
14748 /**
14749  * @class Roo.state.Provider
14750  * Abstract base class for state provider implementations. This class provides methods
14751  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14752  * Provider interface.
14753  */
14754 Roo.state.Provider = function(){
14755     /**
14756      * @event statechange
14757      * Fires when a state change occurs.
14758      * @param {Provider} this This state provider
14759      * @param {String} key The state key which was changed
14760      * @param {String} value The encoded value for the state
14761      */
14762     this.addEvents({
14763         "statechange": true
14764     });
14765     this.state = {};
14766     Roo.state.Provider.superclass.constructor.call(this);
14767 };
14768 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14769     /**
14770      * Returns the current value for a key
14771      * @param {String} name The key name
14772      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14773      * @return {Mixed} The state data
14774      */
14775     get : function(name, defaultValue){
14776         return typeof this.state[name] == "undefined" ?
14777             defaultValue : this.state[name];
14778     },
14779     
14780     /**
14781      * Clears a value from the state
14782      * @param {String} name The key name
14783      */
14784     clear : function(name){
14785         delete this.state[name];
14786         this.fireEvent("statechange", this, name, null);
14787     },
14788     
14789     /**
14790      * Sets the value for a key
14791      * @param {String} name The key name
14792      * @param {Mixed} value The value to set
14793      */
14794     set : function(name, value){
14795         this.state[name] = value;
14796         this.fireEvent("statechange", this, name, value);
14797     },
14798     
14799     /**
14800      * Decodes a string previously encoded with {@link #encodeValue}.
14801      * @param {String} value The value to decode
14802      * @return {Mixed} The decoded value
14803      */
14804     decodeValue : function(cookie){
14805         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14806         var matches = re.exec(unescape(cookie));
14807         if(!matches || !matches[1]) return; // non state cookie
14808         var type = matches[1];
14809         var v = matches[2];
14810         switch(type){
14811             case "n":
14812                 return parseFloat(v);
14813             case "d":
14814                 return new Date(Date.parse(v));
14815             case "b":
14816                 return (v == "1");
14817             case "a":
14818                 var all = [];
14819                 var values = v.split("^");
14820                 for(var i = 0, len = values.length; i < len; i++){
14821                     all.push(this.decodeValue(values[i]));
14822                 }
14823                 return all;
14824            case "o":
14825                 var all = {};
14826                 var values = v.split("^");
14827                 for(var i = 0, len = values.length; i < len; i++){
14828                     var kv = values[i].split("=");
14829                     all[kv[0]] = this.decodeValue(kv[1]);
14830                 }
14831                 return all;
14832            default:
14833                 return v;
14834         }
14835     },
14836     
14837     /**
14838      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14839      * @param {Mixed} value The value to encode
14840      * @return {String} The encoded value
14841      */
14842     encodeValue : function(v){
14843         var enc;
14844         if(typeof v == "number"){
14845             enc = "n:" + v;
14846         }else if(typeof v == "boolean"){
14847             enc = "b:" + (v ? "1" : "0");
14848         }else if(v instanceof Date){
14849             enc = "d:" + v.toGMTString();
14850         }else if(v instanceof Array){
14851             var flat = "";
14852             for(var i = 0, len = v.length; i < len; i++){
14853                 flat += this.encodeValue(v[i]);
14854                 if(i != len-1) flat += "^";
14855             }
14856             enc = "a:" + flat;
14857         }else if(typeof v == "object"){
14858             var flat = "";
14859             for(var key in v){
14860                 if(typeof v[key] != "function"){
14861                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14862                 }
14863             }
14864             enc = "o:" + flat.substring(0, flat.length-1);
14865         }else{
14866             enc = "s:" + v;
14867         }
14868         return escape(enc);        
14869     }
14870 });
14871
14872 /*
14873  * Based on:
14874  * Ext JS Library 1.1.1
14875  * Copyright(c) 2006-2007, Ext JS, LLC.
14876  *
14877  * Originally Released Under LGPL - original licence link has changed is not relivant.
14878  *
14879  * Fork - LGPL
14880  * <script type="text/javascript">
14881  */
14882 /**
14883  * @class Roo.state.Manager
14884  * This is the global state manager. By default all components that are "state aware" check this class
14885  * for state information if you don't pass them a custom state provider. In order for this class
14886  * to be useful, it must be initialized with a provider when your application initializes.
14887  <pre><code>
14888 // in your initialization function
14889 init : function(){
14890    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14891    ...
14892    // supposed you have a {@link Roo.BorderLayout}
14893    var layout = new Roo.BorderLayout(...);
14894    layout.restoreState();
14895    // or a {Roo.BasicDialog}
14896    var dialog = new Roo.BasicDialog(...);
14897    dialog.restoreState();
14898  </code></pre>
14899  * @singleton
14900  */
14901 Roo.state.Manager = function(){
14902     var provider = new Roo.state.Provider();
14903     
14904     return {
14905         /**
14906          * Configures the default state provider for your application
14907          * @param {Provider} stateProvider The state provider to set
14908          */
14909         setProvider : function(stateProvider){
14910             provider = stateProvider;
14911         },
14912         
14913         /**
14914          * Returns the current value for a key
14915          * @param {String} name The key name
14916          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14917          * @return {Mixed} The state data
14918          */
14919         get : function(key, defaultValue){
14920             return provider.get(key, defaultValue);
14921         },
14922         
14923         /**
14924          * Sets the value for a key
14925          * @param {String} name The key name
14926          * @param {Mixed} value The state data
14927          */
14928          set : function(key, value){
14929             provider.set(key, value);
14930         },
14931         
14932         /**
14933          * Clears a value from the state
14934          * @param {String} name The key name
14935          */
14936         clear : function(key){
14937             provider.clear(key);
14938         },
14939         
14940         /**
14941          * Gets the currently configured state provider
14942          * @return {Provider} The state provider
14943          */
14944         getProvider : function(){
14945             return provider;
14946         }
14947     };
14948 }();
14949 /*
14950  * Based on:
14951  * Ext JS Library 1.1.1
14952  * Copyright(c) 2006-2007, Ext JS, LLC.
14953  *
14954  * Originally Released Under LGPL - original licence link has changed is not relivant.
14955  *
14956  * Fork - LGPL
14957  * <script type="text/javascript">
14958  */
14959 /**
14960  * @class Roo.state.CookieProvider
14961  * @extends Roo.state.Provider
14962  * The default Provider implementation which saves state via cookies.
14963  * <br />Usage:
14964  <pre><code>
14965    var cp = new Roo.state.CookieProvider({
14966        path: "/cgi-bin/",
14967        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14968        domain: "roojs.com"
14969    })
14970    Roo.state.Manager.setProvider(cp);
14971  </code></pre>
14972  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14973  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14974  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14975  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14976  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14977  * domain the page is running on including the 'www' like 'www.roojs.com')
14978  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14979  * @constructor
14980  * Create a new CookieProvider
14981  * @param {Object} config The configuration object
14982  */
14983 Roo.state.CookieProvider = function(config){
14984     Roo.state.CookieProvider.superclass.constructor.call(this);
14985     this.path = "/";
14986     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14987     this.domain = null;
14988     this.secure = false;
14989     Roo.apply(this, config);
14990     this.state = this.readCookies();
14991 };
14992
14993 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14994     // private
14995     set : function(name, value){
14996         if(typeof value == "undefined" || value === null){
14997             this.clear(name);
14998             return;
14999         }
15000         this.setCookie(name, value);
15001         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15002     },
15003
15004     // private
15005     clear : function(name){
15006         this.clearCookie(name);
15007         Roo.state.CookieProvider.superclass.clear.call(this, name);
15008     },
15009
15010     // private
15011     readCookies : function(){
15012         var cookies = {};
15013         var c = document.cookie + ";";
15014         var re = /\s?(.*?)=(.*?);/g;
15015         var matches;
15016         while((matches = re.exec(c)) != null){
15017             var name = matches[1];
15018             var value = matches[2];
15019             if(name && name.substring(0,3) == "ys-"){
15020                 cookies[name.substr(3)] = this.decodeValue(value);
15021             }
15022         }
15023         return cookies;
15024     },
15025
15026     // private
15027     setCookie : function(name, value){
15028         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15029            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15030            ((this.path == null) ? "" : ("; path=" + this.path)) +
15031            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15032            ((this.secure == true) ? "; secure" : "");
15033     },
15034
15035     // private
15036     clearCookie : function(name){
15037         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15038            ((this.path == null) ? "" : ("; path=" + this.path)) +
15039            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15040            ((this.secure == true) ? "; secure" : "");
15041     }
15042 });/*
15043  * Based on:
15044  * Ext JS Library 1.1.1
15045  * Copyright(c) 2006-2007, Ext JS, LLC.
15046  *
15047  * Originally Released Under LGPL - original licence link has changed is not relivant.
15048  *
15049  * Fork - LGPL
15050  * <script type="text/javascript">
15051  */
15052  
15053
15054 /**
15055  * @class Roo.ComponentMgr
15056  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15057  * @singleton
15058  */
15059 Roo.ComponentMgr = function(){
15060     var all = new Roo.util.MixedCollection();
15061
15062     return {
15063         /**
15064          * Registers a component.
15065          * @param {Roo.Component} c The component
15066          */
15067         register : function(c){
15068             all.add(c);
15069         },
15070
15071         /**
15072          * Unregisters a component.
15073          * @param {Roo.Component} c The component
15074          */
15075         unregister : function(c){
15076             all.remove(c);
15077         },
15078
15079         /**
15080          * Returns a component by id
15081          * @param {String} id The component id
15082          */
15083         get : function(id){
15084             return all.get(id);
15085         },
15086
15087         /**
15088          * Registers a function that will be called when a specified component is added to ComponentMgr
15089          * @param {String} id The component id
15090          * @param {Funtction} fn The callback function
15091          * @param {Object} scope The scope of the callback
15092          */
15093         onAvailable : function(id, fn, scope){
15094             all.on("add", function(index, o){
15095                 if(o.id == id){
15096                     fn.call(scope || o, o);
15097                     all.un("add", fn, scope);
15098                 }
15099             });
15100         }
15101     };
15102 }();/*
15103  * Based on:
15104  * Ext JS Library 1.1.1
15105  * Copyright(c) 2006-2007, Ext JS, LLC.
15106  *
15107  * Originally Released Under LGPL - original licence link has changed is not relivant.
15108  *
15109  * Fork - LGPL
15110  * <script type="text/javascript">
15111  */
15112  
15113 /**
15114  * @class Roo.Component
15115  * @extends Roo.util.Observable
15116  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15117  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15118  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15119  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15120  * All visual components (widgets) that require rendering into a layout should subclass Component.
15121  * @constructor
15122  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15123  * 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
15124  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15125  */
15126 Roo.Component = function(config){
15127     config = config || {};
15128     if(config.tagName || config.dom || typeof config == "string"){ // element object
15129         config = {el: config, id: config.id || config};
15130     }
15131     this.initialConfig = config;
15132
15133     Roo.apply(this, config);
15134     this.addEvents({
15135         /**
15136          * @event disable
15137          * Fires after the component is disabled.
15138              * @param {Roo.Component} this
15139              */
15140         disable : true,
15141         /**
15142          * @event enable
15143          * Fires after the component is enabled.
15144              * @param {Roo.Component} this
15145              */
15146         enable : true,
15147         /**
15148          * @event beforeshow
15149          * Fires before the component is shown.  Return false to stop the show.
15150              * @param {Roo.Component} this
15151              */
15152         beforeshow : true,
15153         /**
15154          * @event show
15155          * Fires after the component is shown.
15156              * @param {Roo.Component} this
15157              */
15158         show : true,
15159         /**
15160          * @event beforehide
15161          * Fires before the component is hidden. Return false to stop the hide.
15162              * @param {Roo.Component} this
15163              */
15164         beforehide : true,
15165         /**
15166          * @event hide
15167          * Fires after the component is hidden.
15168              * @param {Roo.Component} this
15169              */
15170         hide : true,
15171         /**
15172          * @event beforerender
15173          * Fires before the component is rendered. Return false to stop the render.
15174              * @param {Roo.Component} this
15175              */
15176         beforerender : true,
15177         /**
15178          * @event render
15179          * Fires after the component is rendered.
15180              * @param {Roo.Component} this
15181              */
15182         render : true,
15183         /**
15184          * @event beforedestroy
15185          * Fires before the component is destroyed. Return false to stop the destroy.
15186              * @param {Roo.Component} this
15187              */
15188         beforedestroy : true,
15189         /**
15190          * @event destroy
15191          * Fires after the component is destroyed.
15192              * @param {Roo.Component} this
15193              */
15194         destroy : true
15195     });
15196     if(!this.id){
15197         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15198     }
15199     Roo.ComponentMgr.register(this);
15200     Roo.Component.superclass.constructor.call(this);
15201     this.initComponent();
15202     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15203         this.render(this.renderTo);
15204         delete this.renderTo;
15205     }
15206 };
15207
15208 /** @private */
15209 Roo.Component.AUTO_ID = 1000;
15210
15211 Roo.extend(Roo.Component, Roo.util.Observable, {
15212     /**
15213      * @scope Roo.Component.prototype
15214      * @type {Boolean}
15215      * true if this component is hidden. Read-only.
15216      */
15217     hidden : false,
15218     /**
15219      * @type {Boolean}
15220      * true if this component is disabled. Read-only.
15221      */
15222     disabled : false,
15223     /**
15224      * @type {Boolean}
15225      * true if this component has been rendered. Read-only.
15226      */
15227     rendered : false,
15228     
15229     /** @cfg {String} disableClass
15230      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15231      */
15232     disabledClass : "x-item-disabled",
15233         /** @cfg {Boolean} allowDomMove
15234          * Whether the component can move the Dom node when rendering (defaults to true).
15235          */
15236     allowDomMove : true,
15237     /** @cfg {String} hideMode (display|visibility)
15238      * How this component should hidden. Supported values are
15239      * "visibility" (css visibility), "offsets" (negative offset position) and
15240      * "display" (css display) - defaults to "display".
15241      */
15242     hideMode: 'display',
15243
15244     /** @private */
15245     ctype : "Roo.Component",
15246
15247     /**
15248      * @cfg {String} actionMode 
15249      * which property holds the element that used for  hide() / show() / disable() / enable()
15250      * default is 'el' 
15251      */
15252     actionMode : "el",
15253
15254     /** @private */
15255     getActionEl : function(){
15256         return this[this.actionMode];
15257     },
15258
15259     initComponent : Roo.emptyFn,
15260     /**
15261      * If this is a lazy rendering component, render it to its container element.
15262      * @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.
15263      */
15264     render : function(container, position){
15265         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15266             if(!container && this.el){
15267                 this.el = Roo.get(this.el);
15268                 container = this.el.dom.parentNode;
15269                 this.allowDomMove = false;
15270             }
15271             this.container = Roo.get(container);
15272             this.rendered = true;
15273             if(position !== undefined){
15274                 if(typeof position == 'number'){
15275                     position = this.container.dom.childNodes[position];
15276                 }else{
15277                     position = Roo.getDom(position);
15278                 }
15279             }
15280             this.onRender(this.container, position || null);
15281             if(this.cls){
15282                 this.el.addClass(this.cls);
15283                 delete this.cls;
15284             }
15285             if(this.style){
15286                 this.el.applyStyles(this.style);
15287                 delete this.style;
15288             }
15289             this.fireEvent("render", this);
15290             this.afterRender(this.container);
15291             if(this.hidden){
15292                 this.hide();
15293             }
15294             if(this.disabled){
15295                 this.disable();
15296             }
15297         }
15298         return this;
15299     },
15300
15301     /** @private */
15302     // default function is not really useful
15303     onRender : function(ct, position){
15304         if(this.el){
15305             this.el = Roo.get(this.el);
15306             if(this.allowDomMove !== false){
15307                 ct.dom.insertBefore(this.el.dom, position);
15308             }
15309         }
15310     },
15311
15312     /** @private */
15313     getAutoCreate : function(){
15314         var cfg = typeof this.autoCreate == "object" ?
15315                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15316         if(this.id && !cfg.id){
15317             cfg.id = this.id;
15318         }
15319         return cfg;
15320     },
15321
15322     /** @private */
15323     afterRender : Roo.emptyFn,
15324
15325     /**
15326      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15327      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15328      */
15329     destroy : function(){
15330         if(this.fireEvent("beforedestroy", this) !== false){
15331             this.purgeListeners();
15332             this.beforeDestroy();
15333             if(this.rendered){
15334                 this.el.removeAllListeners();
15335                 this.el.remove();
15336                 if(this.actionMode == "container"){
15337                     this.container.remove();
15338                 }
15339             }
15340             this.onDestroy();
15341             Roo.ComponentMgr.unregister(this);
15342             this.fireEvent("destroy", this);
15343         }
15344     },
15345
15346         /** @private */
15347     beforeDestroy : function(){
15348
15349     },
15350
15351         /** @private */
15352         onDestroy : function(){
15353
15354     },
15355
15356     /**
15357      * Returns the underlying {@link Roo.Element}.
15358      * @return {Roo.Element} The element
15359      */
15360     getEl : function(){
15361         return this.el;
15362     },
15363
15364     /**
15365      * Returns the id of this component.
15366      * @return {String}
15367      */
15368     getId : function(){
15369         return this.id;
15370     },
15371
15372     /**
15373      * Try to focus this component.
15374      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15375      * @return {Roo.Component} this
15376      */
15377     focus : function(selectText){
15378         if(this.rendered){
15379             this.el.focus();
15380             if(selectText === true){
15381                 this.el.dom.select();
15382             }
15383         }
15384         return this;
15385     },
15386
15387     /** @private */
15388     blur : function(){
15389         if(this.rendered){
15390             this.el.blur();
15391         }
15392         return this;
15393     },
15394
15395     /**
15396      * Disable this component.
15397      * @return {Roo.Component} this
15398      */
15399     disable : function(){
15400         if(this.rendered){
15401             this.onDisable();
15402         }
15403         this.disabled = true;
15404         this.fireEvent("disable", this);
15405         return this;
15406     },
15407
15408         // private
15409     onDisable : function(){
15410         this.getActionEl().addClass(this.disabledClass);
15411         this.el.dom.disabled = true;
15412     },
15413
15414     /**
15415      * Enable this component.
15416      * @return {Roo.Component} this
15417      */
15418     enable : function(){
15419         if(this.rendered){
15420             this.onEnable();
15421         }
15422         this.disabled = false;
15423         this.fireEvent("enable", this);
15424         return this;
15425     },
15426
15427         // private
15428     onEnable : function(){
15429         this.getActionEl().removeClass(this.disabledClass);
15430         this.el.dom.disabled = false;
15431     },
15432
15433     /**
15434      * Convenience function for setting disabled/enabled by boolean.
15435      * @param {Boolean} disabled
15436      */
15437     setDisabled : function(disabled){
15438         this[disabled ? "disable" : "enable"]();
15439     },
15440
15441     /**
15442      * Show this component.
15443      * @return {Roo.Component} this
15444      */
15445     show: function(){
15446         if(this.fireEvent("beforeshow", this) !== false){
15447             this.hidden = false;
15448             if(this.rendered){
15449                 this.onShow();
15450             }
15451             this.fireEvent("show", this);
15452         }
15453         return this;
15454     },
15455
15456     // private
15457     onShow : function(){
15458         var ae = this.getActionEl();
15459         if(this.hideMode == 'visibility'){
15460             ae.dom.style.visibility = "visible";
15461         }else if(this.hideMode == 'offsets'){
15462             ae.removeClass('x-hidden');
15463         }else{
15464             ae.dom.style.display = "";
15465         }
15466     },
15467
15468     /**
15469      * Hide this component.
15470      * @return {Roo.Component} this
15471      */
15472     hide: function(){
15473         if(this.fireEvent("beforehide", this) !== false){
15474             this.hidden = true;
15475             if(this.rendered){
15476                 this.onHide();
15477             }
15478             this.fireEvent("hide", this);
15479         }
15480         return this;
15481     },
15482
15483     // private
15484     onHide : function(){
15485         var ae = this.getActionEl();
15486         if(this.hideMode == 'visibility'){
15487             ae.dom.style.visibility = "hidden";
15488         }else if(this.hideMode == 'offsets'){
15489             ae.addClass('x-hidden');
15490         }else{
15491             ae.dom.style.display = "none";
15492         }
15493     },
15494
15495     /**
15496      * Convenience function to hide or show this component by boolean.
15497      * @param {Boolean} visible True to show, false to hide
15498      * @return {Roo.Component} this
15499      */
15500     setVisible: function(visible){
15501         if(visible) {
15502             this.show();
15503         }else{
15504             this.hide();
15505         }
15506         return this;
15507     },
15508
15509     /**
15510      * Returns true if this component is visible.
15511      */
15512     isVisible : function(){
15513         return this.getActionEl().isVisible();
15514     },
15515
15516     cloneConfig : function(overrides){
15517         overrides = overrides || {};
15518         var id = overrides.id || Roo.id();
15519         var cfg = Roo.applyIf(overrides, this.initialConfig);
15520         cfg.id = id; // prevent dup id
15521         return new this.constructor(cfg);
15522     }
15523 });/*
15524  * Based on:
15525  * Ext JS Library 1.1.1
15526  * Copyright(c) 2006-2007, Ext JS, LLC.
15527  *
15528  * Originally Released Under LGPL - original licence link has changed is not relivant.
15529  *
15530  * Fork - LGPL
15531  * <script type="text/javascript">
15532  */
15533
15534 /**
15535  * @class Roo.BoxComponent
15536  * @extends Roo.Component
15537  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15538  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15539  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15540  * layout containers.
15541  * @constructor
15542  * @param {Roo.Element/String/Object} config The configuration options.
15543  */
15544 Roo.BoxComponent = function(config){
15545     Roo.Component.call(this, config);
15546     this.addEvents({
15547         /**
15548          * @event resize
15549          * Fires after the component is resized.
15550              * @param {Roo.Component} this
15551              * @param {Number} adjWidth The box-adjusted width that was set
15552              * @param {Number} adjHeight The box-adjusted height that was set
15553              * @param {Number} rawWidth The width that was originally specified
15554              * @param {Number} rawHeight The height that was originally specified
15555              */
15556         resize : true,
15557         /**
15558          * @event move
15559          * Fires after the component is moved.
15560              * @param {Roo.Component} this
15561              * @param {Number} x The new x position
15562              * @param {Number} y The new y position
15563              */
15564         move : true
15565     });
15566 };
15567
15568 Roo.extend(Roo.BoxComponent, Roo.Component, {
15569     // private, set in afterRender to signify that the component has been rendered
15570     boxReady : false,
15571     // private, used to defer height settings to subclasses
15572     deferHeight: false,
15573     /** @cfg {Number} width
15574      * width (optional) size of component
15575      */
15576      /** @cfg {Number} height
15577      * height (optional) size of component
15578      */
15579      
15580     /**
15581      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15582      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15583      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15584      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15585      * @return {Roo.BoxComponent} this
15586      */
15587     setSize : function(w, h){
15588         // support for standard size objects
15589         if(typeof w == 'object'){
15590             h = w.height;
15591             w = w.width;
15592         }
15593         // not rendered
15594         if(!this.boxReady){
15595             this.width = w;
15596             this.height = h;
15597             return this;
15598         }
15599
15600         // prevent recalcs when not needed
15601         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15602             return this;
15603         }
15604         this.lastSize = {width: w, height: h};
15605
15606         var adj = this.adjustSize(w, h);
15607         var aw = adj.width, ah = adj.height;
15608         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15609             var rz = this.getResizeEl();
15610             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15611                 rz.setSize(aw, ah);
15612             }else if(!this.deferHeight && ah !== undefined){
15613                 rz.setHeight(ah);
15614             }else if(aw !== undefined){
15615                 rz.setWidth(aw);
15616             }
15617             this.onResize(aw, ah, w, h);
15618             this.fireEvent('resize', this, aw, ah, w, h);
15619         }
15620         return this;
15621     },
15622
15623     /**
15624      * Gets the current size of the component's underlying element.
15625      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15626      */
15627     getSize : function(){
15628         return this.el.getSize();
15629     },
15630
15631     /**
15632      * Gets the current XY position of the component's underlying element.
15633      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15634      * @return {Array} The XY position of the element (e.g., [100, 200])
15635      */
15636     getPosition : function(local){
15637         if(local === true){
15638             return [this.el.getLeft(true), this.el.getTop(true)];
15639         }
15640         return this.xy || this.el.getXY();
15641     },
15642
15643     /**
15644      * Gets the current box measurements of the component's underlying element.
15645      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15646      * @returns {Object} box An object in the format {x, y, width, height}
15647      */
15648     getBox : function(local){
15649         var s = this.el.getSize();
15650         if(local){
15651             s.x = this.el.getLeft(true);
15652             s.y = this.el.getTop(true);
15653         }else{
15654             var xy = this.xy || this.el.getXY();
15655             s.x = xy[0];
15656             s.y = xy[1];
15657         }
15658         return s;
15659     },
15660
15661     /**
15662      * Sets the current box measurements of the component's underlying element.
15663      * @param {Object} box An object in the format {x, y, width, height}
15664      * @returns {Roo.BoxComponent} this
15665      */
15666     updateBox : function(box){
15667         this.setSize(box.width, box.height);
15668         this.setPagePosition(box.x, box.y);
15669         return this;
15670     },
15671
15672     // protected
15673     getResizeEl : function(){
15674         return this.resizeEl || this.el;
15675     },
15676
15677     // protected
15678     getPositionEl : function(){
15679         return this.positionEl || this.el;
15680     },
15681
15682     /**
15683      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15684      * This method fires the move event.
15685      * @param {Number} left The new left
15686      * @param {Number} top The new top
15687      * @returns {Roo.BoxComponent} this
15688      */
15689     setPosition : function(x, y){
15690         this.x = x;
15691         this.y = y;
15692         if(!this.boxReady){
15693             return this;
15694         }
15695         var adj = this.adjustPosition(x, y);
15696         var ax = adj.x, ay = adj.y;
15697
15698         var el = this.getPositionEl();
15699         if(ax !== undefined || ay !== undefined){
15700             if(ax !== undefined && ay !== undefined){
15701                 el.setLeftTop(ax, ay);
15702             }else if(ax !== undefined){
15703                 el.setLeft(ax);
15704             }else if(ay !== undefined){
15705                 el.setTop(ay);
15706             }
15707             this.onPosition(ax, ay);
15708             this.fireEvent('move', this, ax, ay);
15709         }
15710         return this;
15711     },
15712
15713     /**
15714      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15715      * This method fires the move event.
15716      * @param {Number} x The new x position
15717      * @param {Number} y The new y position
15718      * @returns {Roo.BoxComponent} this
15719      */
15720     setPagePosition : function(x, y){
15721         this.pageX = x;
15722         this.pageY = y;
15723         if(!this.boxReady){
15724             return;
15725         }
15726         if(x === undefined || y === undefined){ // cannot translate undefined points
15727             return;
15728         }
15729         var p = this.el.translatePoints(x, y);
15730         this.setPosition(p.left, p.top);
15731         return this;
15732     },
15733
15734     // private
15735     onRender : function(ct, position){
15736         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15737         if(this.resizeEl){
15738             this.resizeEl = Roo.get(this.resizeEl);
15739         }
15740         if(this.positionEl){
15741             this.positionEl = Roo.get(this.positionEl);
15742         }
15743     },
15744
15745     // private
15746     afterRender : function(){
15747         Roo.BoxComponent.superclass.afterRender.call(this);
15748         this.boxReady = true;
15749         this.setSize(this.width, this.height);
15750         if(this.x || this.y){
15751             this.setPosition(this.x, this.y);
15752         }
15753         if(this.pageX || this.pageY){
15754             this.setPagePosition(this.pageX, this.pageY);
15755         }
15756     },
15757
15758     /**
15759      * Force the component's size to recalculate based on the underlying element's current height and width.
15760      * @returns {Roo.BoxComponent} this
15761      */
15762     syncSize : function(){
15763         delete this.lastSize;
15764         this.setSize(this.el.getWidth(), this.el.getHeight());
15765         return this;
15766     },
15767
15768     /**
15769      * Called after the component is resized, this method is empty by default but can be implemented by any
15770      * subclass that needs to perform custom logic after a resize occurs.
15771      * @param {Number} adjWidth The box-adjusted width that was set
15772      * @param {Number} adjHeight The box-adjusted height that was set
15773      * @param {Number} rawWidth The width that was originally specified
15774      * @param {Number} rawHeight The height that was originally specified
15775      */
15776     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15777
15778     },
15779
15780     /**
15781      * Called after the component is moved, this method is empty by default but can be implemented by any
15782      * subclass that needs to perform custom logic after a move occurs.
15783      * @param {Number} x The new x position
15784      * @param {Number} y The new y position
15785      */
15786     onPosition : function(x, y){
15787
15788     },
15789
15790     // private
15791     adjustSize : function(w, h){
15792         if(this.autoWidth){
15793             w = 'auto';
15794         }
15795         if(this.autoHeight){
15796             h = 'auto';
15797         }
15798         return {width : w, height: h};
15799     },
15800
15801     // private
15802     adjustPosition : function(x, y){
15803         return {x : x, y: y};
15804     }
15805 });/*
15806  * Original code for Roojs - LGPL
15807  * <script type="text/javascript">
15808  */
15809  
15810 /**
15811  * @class Roo.XComponent
15812  * A delayed Element creator...
15813  * Or a way to group chunks of interface together.
15814  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15815  *  used in conjunction with XComponent.build() it will create an instance of each element,
15816  *  then call addxtype() to build the User interface.
15817  * 
15818  * Mypart.xyx = new Roo.XComponent({
15819
15820     parent : 'Mypart.xyz', // empty == document.element.!!
15821     order : '001',
15822     name : 'xxxx'
15823     region : 'xxxx'
15824     disabled : function() {} 
15825      
15826     tree : function() { // return an tree of xtype declared components
15827         var MODULE = this;
15828         return 
15829         {
15830             xtype : 'NestedLayoutPanel',
15831             // technicall
15832         }
15833      ]
15834  *})
15835  *
15836  *
15837  * It can be used to build a big heiracy, with parent etc.
15838  * or you can just use this to render a single compoent to a dom element
15839  * MYPART.render(Roo.Element | String(id) | dom_element )
15840  *
15841  *
15842  * Usage patterns.
15843  *
15844  * Classic Roo
15845  *
15846  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15847  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15848  *
15849  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15850  *
15851  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15852  * - if mulitple topModules exist, the last one is defined as the top module.
15853  *
15854  * Embeded Roo
15855  * 
15856  * When the top level or multiple modules are to embedded into a existing HTML page,
15857  * the parent element can container '#id' of the element where the module will be drawn.
15858  *
15859  * Bootstrap Roo
15860  *
15861  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15862  * it relies more on a include mechanism, where sub modules are included into an outer page.
15863  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15864  * 
15865  * Bootstrap Roo Included elements
15866  *
15867  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15868  * hence confusing the component builder as it thinks there are multiple top level elements. 
15869  *
15870  * 
15871  * 
15872  * @extends Roo.util.Observable
15873  * @constructor
15874  * @param cfg {Object} configuration of component
15875  * 
15876  */
15877 Roo.XComponent = function(cfg) {
15878     Roo.apply(this, cfg);
15879     this.addEvents({ 
15880         /**
15881              * @event built
15882              * Fires when this the componnt is built
15883              * @param {Roo.XComponent} c the component
15884              */
15885         'built' : true
15886         
15887     });
15888     this.region = this.region || 'center'; // default..
15889     Roo.XComponent.register(this);
15890     this.modules = false;
15891     this.el = false; // where the layout goes..
15892     
15893     
15894 }
15895 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15896     /**
15897      * @property el
15898      * The created element (with Roo.factory())
15899      * @type {Roo.Layout}
15900      */
15901     el  : false,
15902     
15903     /**
15904      * @property el
15905      * for BC  - use el in new code
15906      * @type {Roo.Layout}
15907      */
15908     panel : false,
15909     
15910     /**
15911      * @property layout
15912      * for BC  - use el in new code
15913      * @type {Roo.Layout}
15914      */
15915     layout : false,
15916     
15917      /**
15918      * @cfg {Function|boolean} disabled
15919      * If this module is disabled by some rule, return true from the funtion
15920      */
15921     disabled : false,
15922     
15923     /**
15924      * @cfg {String} parent 
15925      * Name of parent element which it get xtype added to..
15926      */
15927     parent: false,
15928     
15929     /**
15930      * @cfg {String} order
15931      * Used to set the order in which elements are created (usefull for multiple tabs)
15932      */
15933     
15934     order : false,
15935     /**
15936      * @cfg {String} name
15937      * String to display while loading.
15938      */
15939     name : false,
15940     /**
15941      * @cfg {String} region
15942      * Region to render component to (defaults to center)
15943      */
15944     region : 'center',
15945     
15946     /**
15947      * @cfg {Array} items
15948      * A single item array - the first element is the root of the tree..
15949      * It's done this way to stay compatible with the Xtype system...
15950      */
15951     items : false,
15952     
15953     /**
15954      * @property _tree
15955      * The method that retuns the tree of parts that make up this compoennt 
15956      * @type {function}
15957      */
15958     _tree  : false,
15959     
15960      /**
15961      * render
15962      * render element to dom or tree
15963      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15964      */
15965     
15966     render : function(el)
15967     {
15968         
15969         el = el || false;
15970         var hp = this.parent ? 1 : 0;
15971         Roo.debug &&  Roo.log(this);
15972         
15973         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15974             // if parent is a '#.....' string, then let's use that..
15975             var ename = this.parent.substr(1);
15976             this.parent = false;
15977             Roo.debug && Roo.log(ename);
15978             switch (ename) {
15979                 case 'bootstrap-body' :
15980                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15981                         this.parent = { el :  new  Roo.bootstrap.Body() };
15982                         Roo.debug && Roo.log("setting el to doc body");
15983                          
15984                     } else {
15985                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15986                     }
15987                     break;
15988                 case 'bootstrap':
15989                     this.parent = { el : true};
15990                     // fall through
15991                 default:
15992                     el = Roo.get(ename);
15993                     break;
15994             }
15995                 
15996             
15997             if (!el && !this.parent) {
15998                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15999                 return;
16000             }
16001         }
16002         Roo.debug && Roo.log("EL:");
16003         Roo.debug && Roo.log(el);
16004         Roo.debug && Roo.log("this.parent.el:");
16005         Roo.debug && Roo.log(this.parent.el);
16006         
16007         var tree = this._tree ? this._tree() : this.tree();
16008
16009         // altertive root elements ??? - we need a better way to indicate these.
16010         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16011                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16012         
16013         if (!this.parent && is_alt) {
16014             //el = Roo.get(document.body);
16015             this.parent = { el : true };
16016         }
16017             
16018             
16019         
16020         if (!this.parent) {
16021             
16022             Roo.debug && Roo.log("no parent - creating one");
16023             
16024             el = el ? Roo.get(el) : false;      
16025             
16026             // it's a top level one..
16027             this.parent =  {
16028                 el : new Roo.BorderLayout(el || document.body, {
16029                 
16030                      center: {
16031                          titlebar: false,
16032                          autoScroll:false,
16033                          closeOnTab: true,
16034                          tabPosition: 'top',
16035                           //resizeTabs: true,
16036                          alwaysShowTabs: el && hp? false :  true,
16037                          hideTabs: el || !hp ? true :  false,
16038                          minTabWidth: 140
16039                      }
16040                  })
16041             }
16042         }
16043         
16044         if (!this.parent.el) {
16045                 // probably an old style ctor, which has been disabled.
16046                 return;
16047
16048         }
16049                 // The 'tree' method is  '_tree now' 
16050             
16051         tree.region = tree.region || this.region;
16052         
16053         if (this.parent.el === true) {
16054             // bootstrap... - body..
16055             this.parent.el = Roo.factory(tree);
16056         }
16057         
16058         this.el = this.parent.el.addxtype(tree);
16059         this.fireEvent('built', this);
16060         
16061         this.panel = this.el;
16062         this.layout = this.panel.layout;
16063         this.parentLayout = this.parent.layout  || false;  
16064          
16065     }
16066     
16067 });
16068
16069 Roo.apply(Roo.XComponent, {
16070     /**
16071      * @property  hideProgress
16072      * true to disable the building progress bar.. usefull on single page renders.
16073      * @type Boolean
16074      */
16075     hideProgress : false,
16076     /**
16077      * @property  buildCompleted
16078      * True when the builder has completed building the interface.
16079      * @type Boolean
16080      */
16081     buildCompleted : false,
16082      
16083     /**
16084      * @property  topModule
16085      * the upper most module - uses document.element as it's constructor.
16086      * @type Object
16087      */
16088      
16089     topModule  : false,
16090       
16091     /**
16092      * @property  modules
16093      * array of modules to be created by registration system.
16094      * @type {Array} of Roo.XComponent
16095      */
16096     
16097     modules : [],
16098     /**
16099      * @property  elmodules
16100      * array of modules to be created by which use #ID 
16101      * @type {Array} of Roo.XComponent
16102      */
16103      
16104     elmodules : [],
16105
16106      /**
16107      * @property  build_from_html
16108      * Build elements from html - used by bootstrap HTML stuff 
16109      *    - this is cleared after build is completed
16110      * @type {boolean} true  (default false)
16111      */
16112      
16113     build_from_html : false,
16114
16115     /**
16116      * Register components to be built later.
16117      *
16118      * This solves the following issues
16119      * - Building is not done on page load, but after an authentication process has occured.
16120      * - Interface elements are registered on page load
16121      * - Parent Interface elements may not be loaded before child, so this handles that..
16122      * 
16123      *
16124      * example:
16125      * 
16126      * MyApp.register({
16127           order : '000001',
16128           module : 'Pman.Tab.projectMgr',
16129           region : 'center',
16130           parent : 'Pman.layout',
16131           disabled : false,  // or use a function..
16132         })
16133      
16134      * * @param {Object} details about module
16135      */
16136     register : function(obj) {
16137                 
16138         Roo.XComponent.event.fireEvent('register', obj);
16139         switch(typeof(obj.disabled) ) {
16140                 
16141             case 'undefined':
16142                 break;
16143             
16144             case 'function':
16145                 if ( obj.disabled() ) {
16146                         return;
16147                 }
16148                 break;
16149             
16150             default:
16151                 if (obj.disabled) {
16152                         return;
16153                 }
16154                 break;
16155         }
16156                 
16157         this.modules.push(obj);
16158          
16159     },
16160     /**
16161      * convert a string to an object..
16162      * eg. 'AAA.BBB' -> finds AAA.BBB
16163
16164      */
16165     
16166     toObject : function(str)
16167     {
16168         if (!str || typeof(str) == 'object') {
16169             return str;
16170         }
16171         if (str.substring(0,1) == '#') {
16172             return str;
16173         }
16174
16175         var ar = str.split('.');
16176         var rt, o;
16177         rt = ar.shift();
16178             /** eval:var:o */
16179         try {
16180             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16181         } catch (e) {
16182             throw "Module not found : " + str;
16183         }
16184         
16185         if (o === false) {
16186             throw "Module not found : " + str;
16187         }
16188         Roo.each(ar, function(e) {
16189             if (typeof(o[e]) == 'undefined') {
16190                 throw "Module not found : " + str;
16191             }
16192             o = o[e];
16193         });
16194         
16195         return o;
16196         
16197     },
16198     
16199     
16200     /**
16201      * move modules into their correct place in the tree..
16202      * 
16203      */
16204     preBuild : function ()
16205     {
16206         var _t = this;
16207         Roo.each(this.modules , function (obj)
16208         {
16209             Roo.XComponent.event.fireEvent('beforebuild', obj);
16210             
16211             var opar = obj.parent;
16212             try { 
16213                 obj.parent = this.toObject(opar);
16214             } catch(e) {
16215                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16216                 return;
16217             }
16218             
16219             if (!obj.parent) {
16220                 Roo.debug && Roo.log("GOT top level module");
16221                 Roo.debug && Roo.log(obj);
16222                 obj.modules = new Roo.util.MixedCollection(false, 
16223                     function(o) { return o.order + '' }
16224                 );
16225                 this.topModule = obj;
16226                 return;
16227             }
16228                         // parent is a string (usually a dom element name..)
16229             if (typeof(obj.parent) == 'string') {
16230                 this.elmodules.push(obj);
16231                 return;
16232             }
16233             if (obj.parent.constructor != Roo.XComponent) {
16234                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16235             }
16236             if (!obj.parent.modules) {
16237                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16238                     function(o) { return o.order + '' }
16239                 );
16240             }
16241             if (obj.parent.disabled) {
16242                 obj.disabled = true;
16243             }
16244             obj.parent.modules.add(obj);
16245         }, this);
16246     },
16247     
16248      /**
16249      * make a list of modules to build.
16250      * @return {Array} list of modules. 
16251      */ 
16252     
16253     buildOrder : function()
16254     {
16255         var _this = this;
16256         var cmp = function(a,b) {   
16257             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16258         };
16259         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16260             throw "No top level modules to build";
16261         }
16262         
16263         // make a flat list in order of modules to build.
16264         var mods = this.topModule ? [ this.topModule ] : [];
16265                 
16266         
16267         // elmodules (is a list of DOM based modules )
16268         Roo.each(this.elmodules, function(e) {
16269             mods.push(e);
16270             if (!this.topModule &&
16271                 typeof(e.parent) == 'string' &&
16272                 e.parent.substring(0,1) == '#' &&
16273                 Roo.get(e.parent.substr(1))
16274                ) {
16275                 
16276                 _this.topModule = e;
16277             }
16278             
16279         });
16280
16281         
16282         // add modules to their parents..
16283         var addMod = function(m) {
16284             Roo.debug && Roo.log("build Order: add: " + m.name);
16285                 
16286             mods.push(m);
16287             if (m.modules && !m.disabled) {
16288                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16289                 m.modules.keySort('ASC',  cmp );
16290                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16291     
16292                 m.modules.each(addMod);
16293             } else {
16294                 Roo.debug && Roo.log("build Order: no child modules");
16295             }
16296             // not sure if this is used any more..
16297             if (m.finalize) {
16298                 m.finalize.name = m.name + " (clean up) ";
16299                 mods.push(m.finalize);
16300             }
16301             
16302         }
16303         if (this.topModule && this.topModule.modules) { 
16304             this.topModule.modules.keySort('ASC',  cmp );
16305             this.topModule.modules.each(addMod);
16306         } 
16307         return mods;
16308     },
16309     
16310      /**
16311      * Build the registered modules.
16312      * @param {Object} parent element.
16313      * @param {Function} optional method to call after module has been added.
16314      * 
16315      */ 
16316    
16317     build : function(opts) 
16318     {
16319         
16320         if (typeof(opts) != 'undefined') {
16321             Roo.apply(this,opts);
16322         }
16323         
16324         this.preBuild();
16325         var mods = this.buildOrder();
16326       
16327         //this.allmods = mods;
16328         //Roo.debug && Roo.log(mods);
16329         //return;
16330         if (!mods.length) { // should not happen
16331             throw "NO modules!!!";
16332         }
16333         
16334         
16335         var msg = "Building Interface...";
16336         // flash it up as modal - so we store the mask!?
16337         if (!this.hideProgress && Roo.MessageBox) {
16338             Roo.MessageBox.show({ title: 'loading' });
16339             Roo.MessageBox.show({
16340                title: "Please wait...",
16341                msg: msg,
16342                width:450,
16343                progress:true,
16344                closable:false,
16345                modal: false
16346               
16347             });
16348         }
16349         var total = mods.length;
16350         
16351         var _this = this;
16352         var progressRun = function() {
16353             if (!mods.length) {
16354                 Roo.debug && Roo.log('hide?');
16355                 if (!this.hideProgress && Roo.MessageBox) {
16356                     Roo.MessageBox.hide();
16357                 }
16358                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16359                 
16360                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16361                 
16362                 // THE END...
16363                 return false;   
16364             }
16365             
16366             var m = mods.shift();
16367             
16368             
16369             Roo.debug && Roo.log(m);
16370             // not sure if this is supported any more.. - modules that are are just function
16371             if (typeof(m) == 'function') { 
16372                 m.call(this);
16373                 return progressRun.defer(10, _this);
16374             } 
16375             
16376             
16377             msg = "Building Interface " + (total  - mods.length) + 
16378                     " of " + total + 
16379                     (m.name ? (' - ' + m.name) : '');
16380                         Roo.debug && Roo.log(msg);
16381             if (!this.hideProgress &&  Roo.MessageBox) { 
16382                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16383             }
16384             
16385          
16386             // is the module disabled?
16387             var disabled = (typeof(m.disabled) == 'function') ?
16388                 m.disabled.call(m.module.disabled) : m.disabled;    
16389             
16390             
16391             if (disabled) {
16392                 return progressRun(); // we do not update the display!
16393             }
16394             
16395             // now build 
16396             
16397                         
16398                         
16399             m.render();
16400             // it's 10 on top level, and 1 on others??? why...
16401             return progressRun.defer(10, _this);
16402              
16403         }
16404         progressRun.defer(1, _this);
16405      
16406         
16407         
16408     },
16409         
16410         
16411         /**
16412          * Event Object.
16413          *
16414          *
16415          */
16416         event: false, 
16417     /**
16418          * wrapper for event.on - aliased later..  
16419          * Typically use to register a event handler for register:
16420          *
16421          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16422          *
16423          */
16424     on : false
16425    
16426     
16427     
16428 });
16429
16430 Roo.XComponent.event = new Roo.util.Observable({
16431                 events : { 
16432                         /**
16433                          * @event register
16434                          * Fires when an Component is registered,
16435                          * set the disable property on the Component to stop registration.
16436                          * @param {Roo.XComponent} c the component being registerd.
16437                          * 
16438                          */
16439                         'register' : true,
16440             /**
16441                          * @event beforebuild
16442                          * Fires before each Component is built
16443                          * can be used to apply permissions.
16444                          * @param {Roo.XComponent} c the component being registerd.
16445                          * 
16446                          */
16447                         'beforebuild' : true,
16448                         /**
16449                          * @event buildcomplete
16450                          * Fires on the top level element when all elements have been built
16451                          * @param {Roo.XComponent} the top level component.
16452                          */
16453                         'buildcomplete' : true
16454                         
16455                 }
16456 });
16457
16458 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16459  /*
16460  * Based on:
16461  * Ext JS Library 1.1.1
16462  * Copyright(c) 2006-2007, Ext JS, LLC.
16463  *
16464  * Originally Released Under LGPL - original licence link has changed is not relivant.
16465  *
16466  * Fork - LGPL
16467  * <script type="text/javascript">
16468  */
16469
16470
16471
16472 /*
16473  * These classes are derivatives of the similarly named classes in the YUI Library.
16474  * The original license:
16475  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16476  * Code licensed under the BSD License:
16477  * http://developer.yahoo.net/yui/license.txt
16478  */
16479
16480 (function() {
16481
16482 var Event=Roo.EventManager;
16483 var Dom=Roo.lib.Dom;
16484
16485 /**
16486  * @class Roo.dd.DragDrop
16487  * @extends Roo.util.Observable
16488  * Defines the interface and base operation of items that that can be
16489  * dragged or can be drop targets.  It was designed to be extended, overriding
16490  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16491  * Up to three html elements can be associated with a DragDrop instance:
16492  * <ul>
16493  * <li>linked element: the element that is passed into the constructor.
16494  * This is the element which defines the boundaries for interaction with
16495  * other DragDrop objects.</li>
16496  * <li>handle element(s): The drag operation only occurs if the element that
16497  * was clicked matches a handle element.  By default this is the linked
16498  * element, but there are times that you will want only a portion of the
16499  * linked element to initiate the drag operation, and the setHandleElId()
16500  * method provides a way to define this.</li>
16501  * <li>drag element: this represents the element that would be moved along
16502  * with the cursor during a drag operation.  By default, this is the linked
16503  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16504  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16505  * </li>
16506  * </ul>
16507  * This class should not be instantiated until the onload event to ensure that
16508  * the associated elements are available.
16509  * The following would define a DragDrop obj that would interact with any
16510  * other DragDrop obj in the "group1" group:
16511  * <pre>
16512  *  dd = new Roo.dd.DragDrop("div1", "group1");
16513  * </pre>
16514  * Since none of the event handlers have been implemented, nothing would
16515  * actually happen if you were to run the code above.  Normally you would
16516  * override this class or one of the default implementations, but you can
16517  * also override the methods you want on an instance of the class...
16518  * <pre>
16519  *  dd.onDragDrop = function(e, id) {
16520  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16521  *  }
16522  * </pre>
16523  * @constructor
16524  * @param {String} id of the element that is linked to this instance
16525  * @param {String} sGroup the group of related DragDrop objects
16526  * @param {object} config an object containing configurable attributes
16527  *                Valid properties for DragDrop:
16528  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16529  */
16530 Roo.dd.DragDrop = function(id, sGroup, config) {
16531     if (id) {
16532         this.init(id, sGroup, config);
16533     }
16534     
16535 };
16536
16537 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16538
16539     /**
16540      * The id of the element associated with this object.  This is what we
16541      * refer to as the "linked element" because the size and position of
16542      * this element is used to determine when the drag and drop objects have
16543      * interacted.
16544      * @property id
16545      * @type String
16546      */
16547     id: null,
16548
16549     /**
16550      * Configuration attributes passed into the constructor
16551      * @property config
16552      * @type object
16553      */
16554     config: null,
16555
16556     /**
16557      * The id of the element that will be dragged.  By default this is same
16558      * as the linked element , but could be changed to another element. Ex:
16559      * Roo.dd.DDProxy
16560      * @property dragElId
16561      * @type String
16562      * @private
16563      */
16564     dragElId: null,
16565
16566     /**
16567      * the id of the element that initiates the drag operation.  By default
16568      * this is the linked element, but could be changed to be a child of this
16569      * element.  This lets us do things like only starting the drag when the
16570      * header element within the linked html element is clicked.
16571      * @property handleElId
16572      * @type String
16573      * @private
16574      */
16575     handleElId: null,
16576
16577     /**
16578      * An associative array of HTML tags that will be ignored if clicked.
16579      * @property invalidHandleTypes
16580      * @type {string: string}
16581      */
16582     invalidHandleTypes: null,
16583
16584     /**
16585      * An associative array of ids for elements that will be ignored if clicked
16586      * @property invalidHandleIds
16587      * @type {string: string}
16588      */
16589     invalidHandleIds: null,
16590
16591     /**
16592      * An indexted array of css class names for elements that will be ignored
16593      * if clicked.
16594      * @property invalidHandleClasses
16595      * @type string[]
16596      */
16597     invalidHandleClasses: null,
16598
16599     /**
16600      * The linked element's absolute X position at the time the drag was
16601      * started
16602      * @property startPageX
16603      * @type int
16604      * @private
16605      */
16606     startPageX: 0,
16607
16608     /**
16609      * The linked element's absolute X position at the time the drag was
16610      * started
16611      * @property startPageY
16612      * @type int
16613      * @private
16614      */
16615     startPageY: 0,
16616
16617     /**
16618      * The group defines a logical collection of DragDrop objects that are
16619      * related.  Instances only get events when interacting with other
16620      * DragDrop object in the same group.  This lets us define multiple
16621      * groups using a single DragDrop subclass if we want.
16622      * @property groups
16623      * @type {string: string}
16624      */
16625     groups: null,
16626
16627     /**
16628      * Individual drag/drop instances can be locked.  This will prevent
16629      * onmousedown start drag.
16630      * @property locked
16631      * @type boolean
16632      * @private
16633      */
16634     locked: false,
16635
16636     /**
16637      * Lock this instance
16638      * @method lock
16639      */
16640     lock: function() { this.locked = true; },
16641
16642     /**
16643      * Unlock this instace
16644      * @method unlock
16645      */
16646     unlock: function() { this.locked = false; },
16647
16648     /**
16649      * By default, all insances can be a drop target.  This can be disabled by
16650      * setting isTarget to false.
16651      * @method isTarget
16652      * @type boolean
16653      */
16654     isTarget: true,
16655
16656     /**
16657      * The padding configured for this drag and drop object for calculating
16658      * the drop zone intersection with this object.
16659      * @method padding
16660      * @type int[]
16661      */
16662     padding: null,
16663
16664     /**
16665      * Cached reference to the linked element
16666      * @property _domRef
16667      * @private
16668      */
16669     _domRef: null,
16670
16671     /**
16672      * Internal typeof flag
16673      * @property __ygDragDrop
16674      * @private
16675      */
16676     __ygDragDrop: true,
16677
16678     /**
16679      * Set to true when horizontal contraints are applied
16680      * @property constrainX
16681      * @type boolean
16682      * @private
16683      */
16684     constrainX: false,
16685
16686     /**
16687      * Set to true when vertical contraints are applied
16688      * @property constrainY
16689      * @type boolean
16690      * @private
16691      */
16692     constrainY: false,
16693
16694     /**
16695      * The left constraint
16696      * @property minX
16697      * @type int
16698      * @private
16699      */
16700     minX: 0,
16701
16702     /**
16703      * The right constraint
16704      * @property maxX
16705      * @type int
16706      * @private
16707      */
16708     maxX: 0,
16709
16710     /**
16711      * The up constraint
16712      * @property minY
16713      * @type int
16714      * @type int
16715      * @private
16716      */
16717     minY: 0,
16718
16719     /**
16720      * The down constraint
16721      * @property maxY
16722      * @type int
16723      * @private
16724      */
16725     maxY: 0,
16726
16727     /**
16728      * Maintain offsets when we resetconstraints.  Set to true when you want
16729      * the position of the element relative to its parent to stay the same
16730      * when the page changes
16731      *
16732      * @property maintainOffset
16733      * @type boolean
16734      */
16735     maintainOffset: false,
16736
16737     /**
16738      * Array of pixel locations the element will snap to if we specified a
16739      * horizontal graduation/interval.  This array is generated automatically
16740      * when you define a tick interval.
16741      * @property xTicks
16742      * @type int[]
16743      */
16744     xTicks: null,
16745
16746     /**
16747      * Array of pixel locations the element will snap to if we specified a
16748      * vertical graduation/interval.  This array is generated automatically
16749      * when you define a tick interval.
16750      * @property yTicks
16751      * @type int[]
16752      */
16753     yTicks: null,
16754
16755     /**
16756      * By default the drag and drop instance will only respond to the primary
16757      * button click (left button for a right-handed mouse).  Set to true to
16758      * allow drag and drop to start with any mouse click that is propogated
16759      * by the browser
16760      * @property primaryButtonOnly
16761      * @type boolean
16762      */
16763     primaryButtonOnly: true,
16764
16765     /**
16766      * The availabe property is false until the linked dom element is accessible.
16767      * @property available
16768      * @type boolean
16769      */
16770     available: false,
16771
16772     /**
16773      * By default, drags can only be initiated if the mousedown occurs in the
16774      * region the linked element is.  This is done in part to work around a
16775      * bug in some browsers that mis-report the mousedown if the previous
16776      * mouseup happened outside of the window.  This property is set to true
16777      * if outer handles are defined.
16778      *
16779      * @property hasOuterHandles
16780      * @type boolean
16781      * @default false
16782      */
16783     hasOuterHandles: false,
16784
16785     /**
16786      * Code that executes immediately before the startDrag event
16787      * @method b4StartDrag
16788      * @private
16789      */
16790     b4StartDrag: function(x, y) { },
16791
16792     /**
16793      * Abstract method called after a drag/drop object is clicked
16794      * and the drag or mousedown time thresholds have beeen met.
16795      * @method startDrag
16796      * @param {int} X click location
16797      * @param {int} Y click location
16798      */
16799     startDrag: function(x, y) { /* override this */ },
16800
16801     /**
16802      * Code that executes immediately before the onDrag event
16803      * @method b4Drag
16804      * @private
16805      */
16806     b4Drag: function(e) { },
16807
16808     /**
16809      * Abstract method called during the onMouseMove event while dragging an
16810      * object.
16811      * @method onDrag
16812      * @param {Event} e the mousemove event
16813      */
16814     onDrag: function(e) { /* override this */ },
16815
16816     /**
16817      * Abstract method called when this element fist begins hovering over
16818      * another DragDrop obj
16819      * @method onDragEnter
16820      * @param {Event} e the mousemove event
16821      * @param {String|DragDrop[]} id In POINT mode, the element
16822      * id this is hovering over.  In INTERSECT mode, an array of one or more
16823      * dragdrop items being hovered over.
16824      */
16825     onDragEnter: function(e, id) { /* override this */ },
16826
16827     /**
16828      * Code that executes immediately before the onDragOver event
16829      * @method b4DragOver
16830      * @private
16831      */
16832     b4DragOver: function(e) { },
16833
16834     /**
16835      * Abstract method called when this element is hovering over another
16836      * DragDrop obj
16837      * @method onDragOver
16838      * @param {Event} e the mousemove event
16839      * @param {String|DragDrop[]} id In POINT mode, the element
16840      * id this is hovering over.  In INTERSECT mode, an array of dd items
16841      * being hovered over.
16842      */
16843     onDragOver: function(e, id) { /* override this */ },
16844
16845     /**
16846      * Code that executes immediately before the onDragOut event
16847      * @method b4DragOut
16848      * @private
16849      */
16850     b4DragOut: function(e) { },
16851
16852     /**
16853      * Abstract method called when we are no longer hovering over an element
16854      * @method onDragOut
16855      * @param {Event} e the mousemove event
16856      * @param {String|DragDrop[]} id In POINT mode, the element
16857      * id this was hovering over.  In INTERSECT mode, an array of dd items
16858      * that the mouse is no longer over.
16859      */
16860     onDragOut: function(e, id) { /* override this */ },
16861
16862     /**
16863      * Code that executes immediately before the onDragDrop event
16864      * @method b4DragDrop
16865      * @private
16866      */
16867     b4DragDrop: function(e) { },
16868
16869     /**
16870      * Abstract method called when this item is dropped on another DragDrop
16871      * obj
16872      * @method onDragDrop
16873      * @param {Event} e the mouseup event
16874      * @param {String|DragDrop[]} id In POINT mode, the element
16875      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16876      * was dropped on.
16877      */
16878     onDragDrop: function(e, id) { /* override this */ },
16879
16880     /**
16881      * Abstract method called when this item is dropped on an area with no
16882      * drop target
16883      * @method onInvalidDrop
16884      * @param {Event} e the mouseup event
16885      */
16886     onInvalidDrop: function(e) { /* override this */ },
16887
16888     /**
16889      * Code that executes immediately before the endDrag event
16890      * @method b4EndDrag
16891      * @private
16892      */
16893     b4EndDrag: function(e) { },
16894
16895     /**
16896      * Fired when we are done dragging the object
16897      * @method endDrag
16898      * @param {Event} e the mouseup event
16899      */
16900     endDrag: function(e) { /* override this */ },
16901
16902     /**
16903      * Code executed immediately before the onMouseDown event
16904      * @method b4MouseDown
16905      * @param {Event} e the mousedown event
16906      * @private
16907      */
16908     b4MouseDown: function(e) {  },
16909
16910     /**
16911      * Event handler that fires when a drag/drop obj gets a mousedown
16912      * @method onMouseDown
16913      * @param {Event} e the mousedown event
16914      */
16915     onMouseDown: function(e) { /* override this */ },
16916
16917     /**
16918      * Event handler that fires when a drag/drop obj gets a mouseup
16919      * @method onMouseUp
16920      * @param {Event} e the mouseup event
16921      */
16922     onMouseUp: function(e) { /* override this */ },
16923
16924     /**
16925      * Override the onAvailable method to do what is needed after the initial
16926      * position was determined.
16927      * @method onAvailable
16928      */
16929     onAvailable: function () {
16930     },
16931
16932     /*
16933      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16934      * @type Object
16935      */
16936     defaultPadding : {left:0, right:0, top:0, bottom:0},
16937
16938     /*
16939      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16940  *
16941  * Usage:
16942  <pre><code>
16943  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16944                 { dragElId: "existingProxyDiv" });
16945  dd.startDrag = function(){
16946      this.constrainTo("parent-id");
16947  };
16948  </code></pre>
16949  * Or you can initalize it using the {@link Roo.Element} object:
16950  <pre><code>
16951  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16952      startDrag : function(){
16953          this.constrainTo("parent-id");
16954      }
16955  });
16956  </code></pre>
16957      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16958      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16959      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16960      * an object containing the sides to pad. For example: {right:10, bottom:10}
16961      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16962      */
16963     constrainTo : function(constrainTo, pad, inContent){
16964         if(typeof pad == "number"){
16965             pad = {left: pad, right:pad, top:pad, bottom:pad};
16966         }
16967         pad = pad || this.defaultPadding;
16968         var b = Roo.get(this.getEl()).getBox();
16969         var ce = Roo.get(constrainTo);
16970         var s = ce.getScroll();
16971         var c, cd = ce.dom;
16972         if(cd == document.body){
16973             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16974         }else{
16975             xy = ce.getXY();
16976             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16977         }
16978
16979
16980         var topSpace = b.y - c.y;
16981         var leftSpace = b.x - c.x;
16982
16983         this.resetConstraints();
16984         this.setXConstraint(leftSpace - (pad.left||0), // left
16985                 c.width - leftSpace - b.width - (pad.right||0) //right
16986         );
16987         this.setYConstraint(topSpace - (pad.top||0), //top
16988                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16989         );
16990     },
16991
16992     /**
16993      * Returns a reference to the linked element
16994      * @method getEl
16995      * @return {HTMLElement} the html element
16996      */
16997     getEl: function() {
16998         if (!this._domRef) {
16999             this._domRef = Roo.getDom(this.id);
17000         }
17001
17002         return this._domRef;
17003     },
17004
17005     /**
17006      * Returns a reference to the actual element to drag.  By default this is
17007      * the same as the html element, but it can be assigned to another
17008      * element. An example of this can be found in Roo.dd.DDProxy
17009      * @method getDragEl
17010      * @return {HTMLElement} the html element
17011      */
17012     getDragEl: function() {
17013         return Roo.getDom(this.dragElId);
17014     },
17015
17016     /**
17017      * Sets up the DragDrop object.  Must be called in the constructor of any
17018      * Roo.dd.DragDrop subclass
17019      * @method init
17020      * @param id the id of the linked element
17021      * @param {String} sGroup the group of related items
17022      * @param {object} config configuration attributes
17023      */
17024     init: function(id, sGroup, config) {
17025         this.initTarget(id, sGroup, config);
17026         if (!Roo.isTouch) {
17027             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17028         }
17029         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17030         // Event.on(this.id, "selectstart", Event.preventDefault);
17031     },
17032
17033     /**
17034      * Initializes Targeting functionality only... the object does not
17035      * get a mousedown handler.
17036      * @method initTarget
17037      * @param id the id of the linked element
17038      * @param {String} sGroup the group of related items
17039      * @param {object} config configuration attributes
17040      */
17041     initTarget: function(id, sGroup, config) {
17042
17043         // configuration attributes
17044         this.config = config || {};
17045
17046         // create a local reference to the drag and drop manager
17047         this.DDM = Roo.dd.DDM;
17048         // initialize the groups array
17049         this.groups = {};
17050
17051         // assume that we have an element reference instead of an id if the
17052         // parameter is not a string
17053         if (typeof id !== "string") {
17054             id = Roo.id(id);
17055         }
17056
17057         // set the id
17058         this.id = id;
17059
17060         // add to an interaction group
17061         this.addToGroup((sGroup) ? sGroup : "default");
17062
17063         // We don't want to register this as the handle with the manager
17064         // so we just set the id rather than calling the setter.
17065         this.handleElId = id;
17066
17067         // the linked element is the element that gets dragged by default
17068         this.setDragElId(id);
17069
17070         // by default, clicked anchors will not start drag operations.
17071         this.invalidHandleTypes = { A: "A" };
17072         this.invalidHandleIds = {};
17073         this.invalidHandleClasses = [];
17074
17075         this.applyConfig();
17076
17077         this.handleOnAvailable();
17078     },
17079
17080     /**
17081      * Applies the configuration parameters that were passed into the constructor.
17082      * This is supposed to happen at each level through the inheritance chain.  So
17083      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17084      * DragDrop in order to get all of the parameters that are available in
17085      * each object.
17086      * @method applyConfig
17087      */
17088     applyConfig: function() {
17089
17090         // configurable properties:
17091         //    padding, isTarget, maintainOffset, primaryButtonOnly
17092         this.padding           = this.config.padding || [0, 0, 0, 0];
17093         this.isTarget          = (this.config.isTarget !== false);
17094         this.maintainOffset    = (this.config.maintainOffset);
17095         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17096
17097     },
17098
17099     /**
17100      * Executed when the linked element is available
17101      * @method handleOnAvailable
17102      * @private
17103      */
17104     handleOnAvailable: function() {
17105         this.available = true;
17106         this.resetConstraints();
17107         this.onAvailable();
17108     },
17109
17110      /**
17111      * Configures the padding for the target zone in px.  Effectively expands
17112      * (or reduces) the virtual object size for targeting calculations.
17113      * Supports css-style shorthand; if only one parameter is passed, all sides
17114      * will have that padding, and if only two are passed, the top and bottom
17115      * will have the first param, the left and right the second.
17116      * @method setPadding
17117      * @param {int} iTop    Top pad
17118      * @param {int} iRight  Right pad
17119      * @param {int} iBot    Bot pad
17120      * @param {int} iLeft   Left pad
17121      */
17122     setPadding: function(iTop, iRight, iBot, iLeft) {
17123         // this.padding = [iLeft, iRight, iTop, iBot];
17124         if (!iRight && 0 !== iRight) {
17125             this.padding = [iTop, iTop, iTop, iTop];
17126         } else if (!iBot && 0 !== iBot) {
17127             this.padding = [iTop, iRight, iTop, iRight];
17128         } else {
17129             this.padding = [iTop, iRight, iBot, iLeft];
17130         }
17131     },
17132
17133     /**
17134      * Stores the initial placement of the linked element.
17135      * @method setInitialPosition
17136      * @param {int} diffX   the X offset, default 0
17137      * @param {int} diffY   the Y offset, default 0
17138      */
17139     setInitPosition: function(diffX, diffY) {
17140         var el = this.getEl();
17141
17142         if (!this.DDM.verifyEl(el)) {
17143             return;
17144         }
17145
17146         var dx = diffX || 0;
17147         var dy = diffY || 0;
17148
17149         var p = Dom.getXY( el );
17150
17151         this.initPageX = p[0] - dx;
17152         this.initPageY = p[1] - dy;
17153
17154         this.lastPageX = p[0];
17155         this.lastPageY = p[1];
17156
17157
17158         this.setStartPosition(p);
17159     },
17160
17161     /**
17162      * Sets the start position of the element.  This is set when the obj
17163      * is initialized, the reset when a drag is started.
17164      * @method setStartPosition
17165      * @param pos current position (from previous lookup)
17166      * @private
17167      */
17168     setStartPosition: function(pos) {
17169         var p = pos || Dom.getXY( this.getEl() );
17170         this.deltaSetXY = null;
17171
17172         this.startPageX = p[0];
17173         this.startPageY = p[1];
17174     },
17175
17176     /**
17177      * Add this instance to a group of related drag/drop objects.  All
17178      * instances belong to at least one group, and can belong to as many
17179      * groups as needed.
17180      * @method addToGroup
17181      * @param sGroup {string} the name of the group
17182      */
17183     addToGroup: function(sGroup) {
17184         this.groups[sGroup] = true;
17185         this.DDM.regDragDrop(this, sGroup);
17186     },
17187
17188     /**
17189      * Remove's this instance from the supplied interaction group
17190      * @method removeFromGroup
17191      * @param {string}  sGroup  The group to drop
17192      */
17193     removeFromGroup: function(sGroup) {
17194         if (this.groups[sGroup]) {
17195             delete this.groups[sGroup];
17196         }
17197
17198         this.DDM.removeDDFromGroup(this, sGroup);
17199     },
17200
17201     /**
17202      * Allows you to specify that an element other than the linked element
17203      * will be moved with the cursor during a drag
17204      * @method setDragElId
17205      * @param id {string} the id of the element that will be used to initiate the drag
17206      */
17207     setDragElId: function(id) {
17208         this.dragElId = id;
17209     },
17210
17211     /**
17212      * Allows you to specify a child of the linked element that should be
17213      * used to initiate the drag operation.  An example of this would be if
17214      * you have a content div with text and links.  Clicking anywhere in the
17215      * content area would normally start the drag operation.  Use this method
17216      * to specify that an element inside of the content div is the element
17217      * that starts the drag operation.
17218      * @method setHandleElId
17219      * @param id {string} the id of the element that will be used to
17220      * initiate the drag.
17221      */
17222     setHandleElId: function(id) {
17223         if (typeof id !== "string") {
17224             id = Roo.id(id);
17225         }
17226         this.handleElId = id;
17227         this.DDM.regHandle(this.id, id);
17228     },
17229
17230     /**
17231      * Allows you to set an element outside of the linked element as a drag
17232      * handle
17233      * @method setOuterHandleElId
17234      * @param id the id of the element that will be used to initiate the drag
17235      */
17236     setOuterHandleElId: function(id) {
17237         if (typeof id !== "string") {
17238             id = Roo.id(id);
17239         }
17240         Event.on(id, "mousedown",
17241                 this.handleMouseDown, this);
17242         this.setHandleElId(id);
17243
17244         this.hasOuterHandles = true;
17245     },
17246
17247     /**
17248      * Remove all drag and drop hooks for this element
17249      * @method unreg
17250      */
17251     unreg: function() {
17252         Event.un(this.id, "mousedown",
17253                 this.handleMouseDown);
17254         Event.un(this.id, "touchstart",
17255                 this.handleMouseDown);
17256         this._domRef = null;
17257         this.DDM._remove(this);
17258     },
17259
17260     destroy : function(){
17261         this.unreg();
17262     },
17263
17264     /**
17265      * Returns true if this instance is locked, or the drag drop mgr is locked
17266      * (meaning that all drag/drop is disabled on the page.)
17267      * @method isLocked
17268      * @return {boolean} true if this obj or all drag/drop is locked, else
17269      * false
17270      */
17271     isLocked: function() {
17272         return (this.DDM.isLocked() || this.locked);
17273     },
17274
17275     /**
17276      * Fired when this object is clicked
17277      * @method handleMouseDown
17278      * @param {Event} e
17279      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17280      * @private
17281      */
17282     handleMouseDown: function(e, oDD){
17283      
17284         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17285             //Roo.log('not touch/ button !=0');
17286             return;
17287         }
17288         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17289             return; // double touch..
17290         }
17291         
17292
17293         if (this.isLocked()) {
17294             //Roo.log('locked');
17295             return;
17296         }
17297
17298         this.DDM.refreshCache(this.groups);
17299 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17300         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17301         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17302             //Roo.log('no outer handes or not over target');
17303                 // do nothing.
17304         } else {
17305 //            Roo.log('check validator');
17306             if (this.clickValidator(e)) {
17307 //                Roo.log('validate success');
17308                 // set the initial element position
17309                 this.setStartPosition();
17310
17311
17312                 this.b4MouseDown(e);
17313                 this.onMouseDown(e);
17314
17315                 this.DDM.handleMouseDown(e, this);
17316
17317                 this.DDM.stopEvent(e);
17318             } else {
17319
17320
17321             }
17322         }
17323     },
17324
17325     clickValidator: function(e) {
17326         var target = e.getTarget();
17327         return ( this.isValidHandleChild(target) &&
17328                     (this.id == this.handleElId ||
17329                         this.DDM.handleWasClicked(target, this.id)) );
17330     },
17331
17332     /**
17333      * Allows you to specify a tag name that should not start a drag operation
17334      * when clicked.  This is designed to facilitate embedding links within a
17335      * drag handle that do something other than start the drag.
17336      * @method addInvalidHandleType
17337      * @param {string} tagName the type of element to exclude
17338      */
17339     addInvalidHandleType: function(tagName) {
17340         var type = tagName.toUpperCase();
17341         this.invalidHandleTypes[type] = type;
17342     },
17343
17344     /**
17345      * Lets you to specify an element id for a child of a drag handle
17346      * that should not initiate a drag
17347      * @method addInvalidHandleId
17348      * @param {string} id the element id of the element you wish to ignore
17349      */
17350     addInvalidHandleId: function(id) {
17351         if (typeof id !== "string") {
17352             id = Roo.id(id);
17353         }
17354         this.invalidHandleIds[id] = id;
17355     },
17356
17357     /**
17358      * Lets you specify a css class of elements that will not initiate a drag
17359      * @method addInvalidHandleClass
17360      * @param {string} cssClass the class of the elements you wish to ignore
17361      */
17362     addInvalidHandleClass: function(cssClass) {
17363         this.invalidHandleClasses.push(cssClass);
17364     },
17365
17366     /**
17367      * Unsets an excluded tag name set by addInvalidHandleType
17368      * @method removeInvalidHandleType
17369      * @param {string} tagName the type of element to unexclude
17370      */
17371     removeInvalidHandleType: function(tagName) {
17372         var type = tagName.toUpperCase();
17373         // this.invalidHandleTypes[type] = null;
17374         delete this.invalidHandleTypes[type];
17375     },
17376
17377     /**
17378      * Unsets an invalid handle id
17379      * @method removeInvalidHandleId
17380      * @param {string} id the id of the element to re-enable
17381      */
17382     removeInvalidHandleId: function(id) {
17383         if (typeof id !== "string") {
17384             id = Roo.id(id);
17385         }
17386         delete this.invalidHandleIds[id];
17387     },
17388
17389     /**
17390      * Unsets an invalid css class
17391      * @method removeInvalidHandleClass
17392      * @param {string} cssClass the class of the element(s) you wish to
17393      * re-enable
17394      */
17395     removeInvalidHandleClass: function(cssClass) {
17396         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17397             if (this.invalidHandleClasses[i] == cssClass) {
17398                 delete this.invalidHandleClasses[i];
17399             }
17400         }
17401     },
17402
17403     /**
17404      * Checks the tag exclusion list to see if this click should be ignored
17405      * @method isValidHandleChild
17406      * @param {HTMLElement} node the HTMLElement to evaluate
17407      * @return {boolean} true if this is a valid tag type, false if not
17408      */
17409     isValidHandleChild: function(node) {
17410
17411         var valid = true;
17412         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17413         var nodeName;
17414         try {
17415             nodeName = node.nodeName.toUpperCase();
17416         } catch(e) {
17417             nodeName = node.nodeName;
17418         }
17419         valid = valid && !this.invalidHandleTypes[nodeName];
17420         valid = valid && !this.invalidHandleIds[node.id];
17421
17422         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17423             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17424         }
17425
17426
17427         return valid;
17428
17429     },
17430
17431     /**
17432      * Create the array of horizontal tick marks if an interval was specified
17433      * in setXConstraint().
17434      * @method setXTicks
17435      * @private
17436      */
17437     setXTicks: function(iStartX, iTickSize) {
17438         this.xTicks = [];
17439         this.xTickSize = iTickSize;
17440
17441         var tickMap = {};
17442
17443         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17444             if (!tickMap[i]) {
17445                 this.xTicks[this.xTicks.length] = i;
17446                 tickMap[i] = true;
17447             }
17448         }
17449
17450         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17451             if (!tickMap[i]) {
17452                 this.xTicks[this.xTicks.length] = i;
17453                 tickMap[i] = true;
17454             }
17455         }
17456
17457         this.xTicks.sort(this.DDM.numericSort) ;
17458     },
17459
17460     /**
17461      * Create the array of vertical tick marks if an interval was specified in
17462      * setYConstraint().
17463      * @method setYTicks
17464      * @private
17465      */
17466     setYTicks: function(iStartY, iTickSize) {
17467         this.yTicks = [];
17468         this.yTickSize = iTickSize;
17469
17470         var tickMap = {};
17471
17472         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17473             if (!tickMap[i]) {
17474                 this.yTicks[this.yTicks.length] = i;
17475                 tickMap[i] = true;
17476             }
17477         }
17478
17479         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17480             if (!tickMap[i]) {
17481                 this.yTicks[this.yTicks.length] = i;
17482                 tickMap[i] = true;
17483             }
17484         }
17485
17486         this.yTicks.sort(this.DDM.numericSort) ;
17487     },
17488
17489     /**
17490      * By default, the element can be dragged any place on the screen.  Use
17491      * this method to limit the horizontal travel of the element.  Pass in
17492      * 0,0 for the parameters if you want to lock the drag to the y axis.
17493      * @method setXConstraint
17494      * @param {int} iLeft the number of pixels the element can move to the left
17495      * @param {int} iRight the number of pixels the element can move to the
17496      * right
17497      * @param {int} iTickSize optional parameter for specifying that the
17498      * element
17499      * should move iTickSize pixels at a time.
17500      */
17501     setXConstraint: function(iLeft, iRight, iTickSize) {
17502         this.leftConstraint = iLeft;
17503         this.rightConstraint = iRight;
17504
17505         this.minX = this.initPageX - iLeft;
17506         this.maxX = this.initPageX + iRight;
17507         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17508
17509         this.constrainX = true;
17510     },
17511
17512     /**
17513      * Clears any constraints applied to this instance.  Also clears ticks
17514      * since they can't exist independent of a constraint at this time.
17515      * @method clearConstraints
17516      */
17517     clearConstraints: function() {
17518         this.constrainX = false;
17519         this.constrainY = false;
17520         this.clearTicks();
17521     },
17522
17523     /**
17524      * Clears any tick interval defined for this instance
17525      * @method clearTicks
17526      */
17527     clearTicks: function() {
17528         this.xTicks = null;
17529         this.yTicks = null;
17530         this.xTickSize = 0;
17531         this.yTickSize = 0;
17532     },
17533
17534     /**
17535      * By default, the element can be dragged any place on the screen.  Set
17536      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17537      * parameters if you want to lock the drag to the x axis.
17538      * @method setYConstraint
17539      * @param {int} iUp the number of pixels the element can move up
17540      * @param {int} iDown the number of pixels the element can move down
17541      * @param {int} iTickSize optional parameter for specifying that the
17542      * element should move iTickSize pixels at a time.
17543      */
17544     setYConstraint: function(iUp, iDown, iTickSize) {
17545         this.topConstraint = iUp;
17546         this.bottomConstraint = iDown;
17547
17548         this.minY = this.initPageY - iUp;
17549         this.maxY = this.initPageY + iDown;
17550         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17551
17552         this.constrainY = true;
17553
17554     },
17555
17556     /**
17557      * resetConstraints must be called if you manually reposition a dd element.
17558      * @method resetConstraints
17559      * @param {boolean} maintainOffset
17560      */
17561     resetConstraints: function() {
17562
17563
17564         // Maintain offsets if necessary
17565         if (this.initPageX || this.initPageX === 0) {
17566             // figure out how much this thing has moved
17567             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17568             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17569
17570             this.setInitPosition(dx, dy);
17571
17572         // This is the first time we have detected the element's position
17573         } else {
17574             this.setInitPosition();
17575         }
17576
17577         if (this.constrainX) {
17578             this.setXConstraint( this.leftConstraint,
17579                                  this.rightConstraint,
17580                                  this.xTickSize        );
17581         }
17582
17583         if (this.constrainY) {
17584             this.setYConstraint( this.topConstraint,
17585                                  this.bottomConstraint,
17586                                  this.yTickSize         );
17587         }
17588     },
17589
17590     /**
17591      * Normally the drag element is moved pixel by pixel, but we can specify
17592      * that it move a number of pixels at a time.  This method resolves the
17593      * location when we have it set up like this.
17594      * @method getTick
17595      * @param {int} val where we want to place the object
17596      * @param {int[]} tickArray sorted array of valid points
17597      * @return {int} the closest tick
17598      * @private
17599      */
17600     getTick: function(val, tickArray) {
17601
17602         if (!tickArray) {
17603             // If tick interval is not defined, it is effectively 1 pixel,
17604             // so we return the value passed to us.
17605             return val;
17606         } else if (tickArray[0] >= val) {
17607             // The value is lower than the first tick, so we return the first
17608             // tick.
17609             return tickArray[0];
17610         } else {
17611             for (var i=0, len=tickArray.length; i<len; ++i) {
17612                 var next = i + 1;
17613                 if (tickArray[next] && tickArray[next] >= val) {
17614                     var diff1 = val - tickArray[i];
17615                     var diff2 = tickArray[next] - val;
17616                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17617                 }
17618             }
17619
17620             // The value is larger than the last tick, so we return the last
17621             // tick.
17622             return tickArray[tickArray.length - 1];
17623         }
17624     },
17625
17626     /**
17627      * toString method
17628      * @method toString
17629      * @return {string} string representation of the dd obj
17630      */
17631     toString: function() {
17632         return ("DragDrop " + this.id);
17633     }
17634
17635 });
17636
17637 })();
17638 /*
17639  * Based on:
17640  * Ext JS Library 1.1.1
17641  * Copyright(c) 2006-2007, Ext JS, LLC.
17642  *
17643  * Originally Released Under LGPL - original licence link has changed is not relivant.
17644  *
17645  * Fork - LGPL
17646  * <script type="text/javascript">
17647  */
17648
17649
17650 /**
17651  * The drag and drop utility provides a framework for building drag and drop
17652  * applications.  In addition to enabling drag and drop for specific elements,
17653  * the drag and drop elements are tracked by the manager class, and the
17654  * interactions between the various elements are tracked during the drag and
17655  * the implementing code is notified about these important moments.
17656  */
17657
17658 // Only load the library once.  Rewriting the manager class would orphan
17659 // existing drag and drop instances.
17660 if (!Roo.dd.DragDropMgr) {
17661
17662 /**
17663  * @class Roo.dd.DragDropMgr
17664  * DragDropMgr is a singleton that tracks the element interaction for
17665  * all DragDrop items in the window.  Generally, you will not call
17666  * this class directly, but it does have helper methods that could
17667  * be useful in your DragDrop implementations.
17668  * @singleton
17669  */
17670 Roo.dd.DragDropMgr = function() {
17671
17672     var Event = Roo.EventManager;
17673
17674     return {
17675
17676         /**
17677          * Two dimensional Array of registered DragDrop objects.  The first
17678          * dimension is the DragDrop item group, the second the DragDrop
17679          * object.
17680          * @property ids
17681          * @type {string: string}
17682          * @private
17683          * @static
17684          */
17685         ids: {},
17686
17687         /**
17688          * Array of element ids defined as drag handles.  Used to determine
17689          * if the element that generated the mousedown event is actually the
17690          * handle and not the html element itself.
17691          * @property handleIds
17692          * @type {string: string}
17693          * @private
17694          * @static
17695          */
17696         handleIds: {},
17697
17698         /**
17699          * the DragDrop object that is currently being dragged
17700          * @property dragCurrent
17701          * @type DragDrop
17702          * @private
17703          * @static
17704          **/
17705         dragCurrent: null,
17706
17707         /**
17708          * the DragDrop object(s) that are being hovered over
17709          * @property dragOvers
17710          * @type Array
17711          * @private
17712          * @static
17713          */
17714         dragOvers: {},
17715
17716         /**
17717          * the X distance between the cursor and the object being dragged
17718          * @property deltaX
17719          * @type int
17720          * @private
17721          * @static
17722          */
17723         deltaX: 0,
17724
17725         /**
17726          * the Y distance between the cursor and the object being dragged
17727          * @property deltaY
17728          * @type int
17729          * @private
17730          * @static
17731          */
17732         deltaY: 0,
17733
17734         /**
17735          * Flag to determine if we should prevent the default behavior of the
17736          * events we define. By default this is true, but this can be set to
17737          * false if you need the default behavior (not recommended)
17738          * @property preventDefault
17739          * @type boolean
17740          * @static
17741          */
17742         preventDefault: true,
17743
17744         /**
17745          * Flag to determine if we should stop the propagation of the events
17746          * we generate. This is true by default but you may want to set it to
17747          * false if the html element contains other features that require the
17748          * mouse click.
17749          * @property stopPropagation
17750          * @type boolean
17751          * @static
17752          */
17753         stopPropagation: true,
17754
17755         /**
17756          * Internal flag that is set to true when drag and drop has been
17757          * intialized
17758          * @property initialized
17759          * @private
17760          * @static
17761          */
17762         initalized: false,
17763
17764         /**
17765          * All drag and drop can be disabled.
17766          * @property locked
17767          * @private
17768          * @static
17769          */
17770         locked: false,
17771
17772         /**
17773          * Called the first time an element is registered.
17774          * @method init
17775          * @private
17776          * @static
17777          */
17778         init: function() {
17779             this.initialized = true;
17780         },
17781
17782         /**
17783          * In point mode, drag and drop interaction is defined by the
17784          * location of the cursor during the drag/drop
17785          * @property POINT
17786          * @type int
17787          * @static
17788          */
17789         POINT: 0,
17790
17791         /**
17792          * In intersect mode, drag and drop interactio nis defined by the
17793          * overlap of two or more drag and drop objects.
17794          * @property INTERSECT
17795          * @type int
17796          * @static
17797          */
17798         INTERSECT: 1,
17799
17800         /**
17801          * The current drag and drop mode.  Default: POINT
17802          * @property mode
17803          * @type int
17804          * @static
17805          */
17806         mode: 0,
17807
17808         /**
17809          * Runs method on all drag and drop objects
17810          * @method _execOnAll
17811          * @private
17812          * @static
17813          */
17814         _execOnAll: function(sMethod, args) {
17815             for (var i in this.ids) {
17816                 for (var j in this.ids[i]) {
17817                     var oDD = this.ids[i][j];
17818                     if (! this.isTypeOfDD(oDD)) {
17819                         continue;
17820                     }
17821                     oDD[sMethod].apply(oDD, args);
17822                 }
17823             }
17824         },
17825
17826         /**
17827          * Drag and drop initialization.  Sets up the global event handlers
17828          * @method _onLoad
17829          * @private
17830          * @static
17831          */
17832         _onLoad: function() {
17833
17834             this.init();
17835
17836             if (!Roo.isTouch) {
17837                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17838                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17839             }
17840             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17841             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17842             
17843             Event.on(window,   "unload",    this._onUnload, this, true);
17844             Event.on(window,   "resize",    this._onResize, this, true);
17845             // Event.on(window,   "mouseout",    this._test);
17846
17847         },
17848
17849         /**
17850          * Reset constraints on all drag and drop objs
17851          * @method _onResize
17852          * @private
17853          * @static
17854          */
17855         _onResize: function(e) {
17856             this._execOnAll("resetConstraints", []);
17857         },
17858
17859         /**
17860          * Lock all drag and drop functionality
17861          * @method lock
17862          * @static
17863          */
17864         lock: function() { this.locked = true; },
17865
17866         /**
17867          * Unlock all drag and drop functionality
17868          * @method unlock
17869          * @static
17870          */
17871         unlock: function() { this.locked = false; },
17872
17873         /**
17874          * Is drag and drop locked?
17875          * @method isLocked
17876          * @return {boolean} True if drag and drop is locked, false otherwise.
17877          * @static
17878          */
17879         isLocked: function() { return this.locked; },
17880
17881         /**
17882          * Location cache that is set for all drag drop objects when a drag is
17883          * initiated, cleared when the drag is finished.
17884          * @property locationCache
17885          * @private
17886          * @static
17887          */
17888         locationCache: {},
17889
17890         /**
17891          * Set useCache to false if you want to force object the lookup of each
17892          * drag and drop linked element constantly during a drag.
17893          * @property useCache
17894          * @type boolean
17895          * @static
17896          */
17897         useCache: true,
17898
17899         /**
17900          * The number of pixels that the mouse needs to move after the
17901          * mousedown before the drag is initiated.  Default=3;
17902          * @property clickPixelThresh
17903          * @type int
17904          * @static
17905          */
17906         clickPixelThresh: 3,
17907
17908         /**
17909          * The number of milliseconds after the mousedown event to initiate the
17910          * drag if we don't get a mouseup event. Default=1000
17911          * @property clickTimeThresh
17912          * @type int
17913          * @static
17914          */
17915         clickTimeThresh: 350,
17916
17917         /**
17918          * Flag that indicates that either the drag pixel threshold or the
17919          * mousdown time threshold has been met
17920          * @property dragThreshMet
17921          * @type boolean
17922          * @private
17923          * @static
17924          */
17925         dragThreshMet: false,
17926
17927         /**
17928          * Timeout used for the click time threshold
17929          * @property clickTimeout
17930          * @type Object
17931          * @private
17932          * @static
17933          */
17934         clickTimeout: null,
17935
17936         /**
17937          * The X position of the mousedown event stored for later use when a
17938          * drag threshold is met.
17939          * @property startX
17940          * @type int
17941          * @private
17942          * @static
17943          */
17944         startX: 0,
17945
17946         /**
17947          * The Y position of the mousedown event stored for later use when a
17948          * drag threshold is met.
17949          * @property startY
17950          * @type int
17951          * @private
17952          * @static
17953          */
17954         startY: 0,
17955
17956         /**
17957          * Each DragDrop instance must be registered with the DragDropMgr.
17958          * This is executed in DragDrop.init()
17959          * @method regDragDrop
17960          * @param {DragDrop} oDD the DragDrop object to register
17961          * @param {String} sGroup the name of the group this element belongs to
17962          * @static
17963          */
17964         regDragDrop: function(oDD, sGroup) {
17965             if (!this.initialized) { this.init(); }
17966
17967             if (!this.ids[sGroup]) {
17968                 this.ids[sGroup] = {};
17969             }
17970             this.ids[sGroup][oDD.id] = oDD;
17971         },
17972
17973         /**
17974          * Removes the supplied dd instance from the supplied group. Executed
17975          * by DragDrop.removeFromGroup, so don't call this function directly.
17976          * @method removeDDFromGroup
17977          * @private
17978          * @static
17979          */
17980         removeDDFromGroup: function(oDD, sGroup) {
17981             if (!this.ids[sGroup]) {
17982                 this.ids[sGroup] = {};
17983             }
17984
17985             var obj = this.ids[sGroup];
17986             if (obj && obj[oDD.id]) {
17987                 delete obj[oDD.id];
17988             }
17989         },
17990
17991         /**
17992          * Unregisters a drag and drop item.  This is executed in
17993          * DragDrop.unreg, use that method instead of calling this directly.
17994          * @method _remove
17995          * @private
17996          * @static
17997          */
17998         _remove: function(oDD) {
17999             for (var g in oDD.groups) {
18000                 if (g && this.ids[g][oDD.id]) {
18001                     delete this.ids[g][oDD.id];
18002                 }
18003             }
18004             delete this.handleIds[oDD.id];
18005         },
18006
18007         /**
18008          * Each DragDrop handle element must be registered.  This is done
18009          * automatically when executing DragDrop.setHandleElId()
18010          * @method regHandle
18011          * @param {String} sDDId the DragDrop id this element is a handle for
18012          * @param {String} sHandleId the id of the element that is the drag
18013          * handle
18014          * @static
18015          */
18016         regHandle: function(sDDId, sHandleId) {
18017             if (!this.handleIds[sDDId]) {
18018                 this.handleIds[sDDId] = {};
18019             }
18020             this.handleIds[sDDId][sHandleId] = sHandleId;
18021         },
18022
18023         /**
18024          * Utility function to determine if a given element has been
18025          * registered as a drag drop item.
18026          * @method isDragDrop
18027          * @param {String} id the element id to check
18028          * @return {boolean} true if this element is a DragDrop item,
18029          * false otherwise
18030          * @static
18031          */
18032         isDragDrop: function(id) {
18033             return ( this.getDDById(id) ) ? true : false;
18034         },
18035
18036         /**
18037          * Returns the drag and drop instances that are in all groups the
18038          * passed in instance belongs to.
18039          * @method getRelated
18040          * @param {DragDrop} p_oDD the obj to get related data for
18041          * @param {boolean} bTargetsOnly if true, only return targetable objs
18042          * @return {DragDrop[]} the related instances
18043          * @static
18044          */
18045         getRelated: function(p_oDD, bTargetsOnly) {
18046             var oDDs = [];
18047             for (var i in p_oDD.groups) {
18048                 for (j in this.ids[i]) {
18049                     var dd = this.ids[i][j];
18050                     if (! this.isTypeOfDD(dd)) {
18051                         continue;
18052                     }
18053                     if (!bTargetsOnly || dd.isTarget) {
18054                         oDDs[oDDs.length] = dd;
18055                     }
18056                 }
18057             }
18058
18059             return oDDs;
18060         },
18061
18062         /**
18063          * Returns true if the specified dd target is a legal target for
18064          * the specifice drag obj
18065          * @method isLegalTarget
18066          * @param {DragDrop} the drag obj
18067          * @param {DragDrop} the target
18068          * @return {boolean} true if the target is a legal target for the
18069          * dd obj
18070          * @static
18071          */
18072         isLegalTarget: function (oDD, oTargetDD) {
18073             var targets = this.getRelated(oDD, true);
18074             for (var i=0, len=targets.length;i<len;++i) {
18075                 if (targets[i].id == oTargetDD.id) {
18076                     return true;
18077                 }
18078             }
18079
18080             return false;
18081         },
18082
18083         /**
18084          * My goal is to be able to transparently determine if an object is
18085          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18086          * returns "object", oDD.constructor.toString() always returns
18087          * "DragDrop" and not the name of the subclass.  So for now it just
18088          * evaluates a well-known variable in DragDrop.
18089          * @method isTypeOfDD
18090          * @param {Object} the object to evaluate
18091          * @return {boolean} true if typeof oDD = DragDrop
18092          * @static
18093          */
18094         isTypeOfDD: function (oDD) {
18095             return (oDD && oDD.__ygDragDrop);
18096         },
18097
18098         /**
18099          * Utility function to determine if a given element has been
18100          * registered as a drag drop handle for the given Drag Drop object.
18101          * @method isHandle
18102          * @param {String} id the element id to check
18103          * @return {boolean} true if this element is a DragDrop handle, false
18104          * otherwise
18105          * @static
18106          */
18107         isHandle: function(sDDId, sHandleId) {
18108             return ( this.handleIds[sDDId] &&
18109                             this.handleIds[sDDId][sHandleId] );
18110         },
18111
18112         /**
18113          * Returns the DragDrop instance for a given id
18114          * @method getDDById
18115          * @param {String} id the id of the DragDrop object
18116          * @return {DragDrop} the drag drop object, null if it is not found
18117          * @static
18118          */
18119         getDDById: function(id) {
18120             for (var i in this.ids) {
18121                 if (this.ids[i][id]) {
18122                     return this.ids[i][id];
18123                 }
18124             }
18125             return null;
18126         },
18127
18128         /**
18129          * Fired after a registered DragDrop object gets the mousedown event.
18130          * Sets up the events required to track the object being dragged
18131          * @method handleMouseDown
18132          * @param {Event} e the event
18133          * @param oDD the DragDrop object being dragged
18134          * @private
18135          * @static
18136          */
18137         handleMouseDown: function(e, oDD) {
18138             if(Roo.QuickTips){
18139                 Roo.QuickTips.disable();
18140             }
18141             this.currentTarget = e.getTarget();
18142
18143             this.dragCurrent = oDD;
18144
18145             var el = oDD.getEl();
18146
18147             // track start position
18148             this.startX = e.getPageX();
18149             this.startY = e.getPageY();
18150
18151             this.deltaX = this.startX - el.offsetLeft;
18152             this.deltaY = this.startY - el.offsetTop;
18153
18154             this.dragThreshMet = false;
18155
18156             this.clickTimeout = setTimeout(
18157                     function() {
18158                         var DDM = Roo.dd.DDM;
18159                         DDM.startDrag(DDM.startX, DDM.startY);
18160                     },
18161                     this.clickTimeThresh );
18162         },
18163
18164         /**
18165          * Fired when either the drag pixel threshol or the mousedown hold
18166          * time threshold has been met.
18167          * @method startDrag
18168          * @param x {int} the X position of the original mousedown
18169          * @param y {int} the Y position of the original mousedown
18170          * @static
18171          */
18172         startDrag: function(x, y) {
18173             clearTimeout(this.clickTimeout);
18174             if (this.dragCurrent) {
18175                 this.dragCurrent.b4StartDrag(x, y);
18176                 this.dragCurrent.startDrag(x, y);
18177             }
18178             this.dragThreshMet = true;
18179         },
18180
18181         /**
18182          * Internal function to handle the mouseup event.  Will be invoked
18183          * from the context of the document.
18184          * @method handleMouseUp
18185          * @param {Event} e the event
18186          * @private
18187          * @static
18188          */
18189         handleMouseUp: function(e) {
18190
18191             if(Roo.QuickTips){
18192                 Roo.QuickTips.enable();
18193             }
18194             if (! this.dragCurrent) {
18195                 return;
18196             }
18197
18198             clearTimeout(this.clickTimeout);
18199
18200             if (this.dragThreshMet) {
18201                 this.fireEvents(e, true);
18202             } else {
18203             }
18204
18205             this.stopDrag(e);
18206
18207             this.stopEvent(e);
18208         },
18209
18210         /**
18211          * Utility to stop event propagation and event default, if these
18212          * features are turned on.
18213          * @method stopEvent
18214          * @param {Event} e the event as returned by this.getEvent()
18215          * @static
18216          */
18217         stopEvent: function(e){
18218             if(this.stopPropagation) {
18219                 e.stopPropagation();
18220             }
18221
18222             if (this.preventDefault) {
18223                 e.preventDefault();
18224             }
18225         },
18226
18227         /**
18228          * Internal function to clean up event handlers after the drag
18229          * operation is complete
18230          * @method stopDrag
18231          * @param {Event} e the event
18232          * @private
18233          * @static
18234          */
18235         stopDrag: function(e) {
18236             // Fire the drag end event for the item that was dragged
18237             if (this.dragCurrent) {
18238                 if (this.dragThreshMet) {
18239                     this.dragCurrent.b4EndDrag(e);
18240                     this.dragCurrent.endDrag(e);
18241                 }
18242
18243                 this.dragCurrent.onMouseUp(e);
18244             }
18245
18246             this.dragCurrent = null;
18247             this.dragOvers = {};
18248         },
18249
18250         /**
18251          * Internal function to handle the mousemove event.  Will be invoked
18252          * from the context of the html element.
18253          *
18254          * @TODO figure out what we can do about mouse events lost when the
18255          * user drags objects beyond the window boundary.  Currently we can
18256          * detect this in internet explorer by verifying that the mouse is
18257          * down during the mousemove event.  Firefox doesn't give us the
18258          * button state on the mousemove event.
18259          * @method handleMouseMove
18260          * @param {Event} e the event
18261          * @private
18262          * @static
18263          */
18264         handleMouseMove: function(e) {
18265             if (! this.dragCurrent) {
18266                 return true;
18267             }
18268
18269             // var button = e.which || e.button;
18270
18271             // check for IE mouseup outside of page boundary
18272             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18273                 this.stopEvent(e);
18274                 return this.handleMouseUp(e);
18275             }
18276
18277             if (!this.dragThreshMet) {
18278                 var diffX = Math.abs(this.startX - e.getPageX());
18279                 var diffY = Math.abs(this.startY - e.getPageY());
18280                 if (diffX > this.clickPixelThresh ||
18281                             diffY > this.clickPixelThresh) {
18282                     this.startDrag(this.startX, this.startY);
18283                 }
18284             }
18285
18286             if (this.dragThreshMet) {
18287                 this.dragCurrent.b4Drag(e);
18288                 this.dragCurrent.onDrag(e);
18289                 if(!this.dragCurrent.moveOnly){
18290                     this.fireEvents(e, false);
18291                 }
18292             }
18293
18294             this.stopEvent(e);
18295
18296             return true;
18297         },
18298
18299         /**
18300          * Iterates over all of the DragDrop elements to find ones we are
18301          * hovering over or dropping on
18302          * @method fireEvents
18303          * @param {Event} e the event
18304          * @param {boolean} isDrop is this a drop op or a mouseover op?
18305          * @private
18306          * @static
18307          */
18308         fireEvents: function(e, isDrop) {
18309             var dc = this.dragCurrent;
18310
18311             // If the user did the mouse up outside of the window, we could
18312             // get here even though we have ended the drag.
18313             if (!dc || dc.isLocked()) {
18314                 return;
18315             }
18316
18317             var pt = e.getPoint();
18318
18319             // cache the previous dragOver array
18320             var oldOvers = [];
18321
18322             var outEvts   = [];
18323             var overEvts  = [];
18324             var dropEvts  = [];
18325             var enterEvts = [];
18326
18327             // Check to see if the object(s) we were hovering over is no longer
18328             // being hovered over so we can fire the onDragOut event
18329             for (var i in this.dragOvers) {
18330
18331                 var ddo = this.dragOvers[i];
18332
18333                 if (! this.isTypeOfDD(ddo)) {
18334                     continue;
18335                 }
18336
18337                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18338                     outEvts.push( ddo );
18339                 }
18340
18341                 oldOvers[i] = true;
18342                 delete this.dragOvers[i];
18343             }
18344
18345             for (var sGroup in dc.groups) {
18346
18347                 if ("string" != typeof sGroup) {
18348                     continue;
18349                 }
18350
18351                 for (i in this.ids[sGroup]) {
18352                     var oDD = this.ids[sGroup][i];
18353                     if (! this.isTypeOfDD(oDD)) {
18354                         continue;
18355                     }
18356
18357                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18358                         if (this.isOverTarget(pt, oDD, this.mode)) {
18359                             // look for drop interactions
18360                             if (isDrop) {
18361                                 dropEvts.push( oDD );
18362                             // look for drag enter and drag over interactions
18363                             } else {
18364
18365                                 // initial drag over: dragEnter fires
18366                                 if (!oldOvers[oDD.id]) {
18367                                     enterEvts.push( oDD );
18368                                 // subsequent drag overs: dragOver fires
18369                                 } else {
18370                                     overEvts.push( oDD );
18371                                 }
18372
18373                                 this.dragOvers[oDD.id] = oDD;
18374                             }
18375                         }
18376                     }
18377                 }
18378             }
18379
18380             if (this.mode) {
18381                 if (outEvts.length) {
18382                     dc.b4DragOut(e, outEvts);
18383                     dc.onDragOut(e, outEvts);
18384                 }
18385
18386                 if (enterEvts.length) {
18387                     dc.onDragEnter(e, enterEvts);
18388                 }
18389
18390                 if (overEvts.length) {
18391                     dc.b4DragOver(e, overEvts);
18392                     dc.onDragOver(e, overEvts);
18393                 }
18394
18395                 if (dropEvts.length) {
18396                     dc.b4DragDrop(e, dropEvts);
18397                     dc.onDragDrop(e, dropEvts);
18398                 }
18399
18400             } else {
18401                 // fire dragout events
18402                 var len = 0;
18403                 for (i=0, len=outEvts.length; i<len; ++i) {
18404                     dc.b4DragOut(e, outEvts[i].id);
18405                     dc.onDragOut(e, outEvts[i].id);
18406                 }
18407
18408                 // fire enter events
18409                 for (i=0,len=enterEvts.length; i<len; ++i) {
18410                     // dc.b4DragEnter(e, oDD.id);
18411                     dc.onDragEnter(e, enterEvts[i].id);
18412                 }
18413
18414                 // fire over events
18415                 for (i=0,len=overEvts.length; i<len; ++i) {
18416                     dc.b4DragOver(e, overEvts[i].id);
18417                     dc.onDragOver(e, overEvts[i].id);
18418                 }
18419
18420                 // fire drop events
18421                 for (i=0, len=dropEvts.length; i<len; ++i) {
18422                     dc.b4DragDrop(e, dropEvts[i].id);
18423                     dc.onDragDrop(e, dropEvts[i].id);
18424                 }
18425
18426             }
18427
18428             // notify about a drop that did not find a target
18429             if (isDrop && !dropEvts.length) {
18430                 dc.onInvalidDrop(e);
18431             }
18432
18433         },
18434
18435         /**
18436          * Helper function for getting the best match from the list of drag
18437          * and drop objects returned by the drag and drop events when we are
18438          * in INTERSECT mode.  It returns either the first object that the
18439          * cursor is over, or the object that has the greatest overlap with
18440          * the dragged element.
18441          * @method getBestMatch
18442          * @param  {DragDrop[]} dds The array of drag and drop objects
18443          * targeted
18444          * @return {DragDrop}       The best single match
18445          * @static
18446          */
18447         getBestMatch: function(dds) {
18448             var winner = null;
18449             // Return null if the input is not what we expect
18450             //if (!dds || !dds.length || dds.length == 0) {
18451                // winner = null;
18452             // If there is only one item, it wins
18453             //} else if (dds.length == 1) {
18454
18455             var len = dds.length;
18456
18457             if (len == 1) {
18458                 winner = dds[0];
18459             } else {
18460                 // Loop through the targeted items
18461                 for (var i=0; i<len; ++i) {
18462                     var dd = dds[i];
18463                     // If the cursor is over the object, it wins.  If the
18464                     // cursor is over multiple matches, the first one we come
18465                     // to wins.
18466                     if (dd.cursorIsOver) {
18467                         winner = dd;
18468                         break;
18469                     // Otherwise the object with the most overlap wins
18470                     } else {
18471                         if (!winner ||
18472                             winner.overlap.getArea() < dd.overlap.getArea()) {
18473                             winner = dd;
18474                         }
18475                     }
18476                 }
18477             }
18478
18479             return winner;
18480         },
18481
18482         /**
18483          * Refreshes the cache of the top-left and bottom-right points of the
18484          * drag and drop objects in the specified group(s).  This is in the
18485          * format that is stored in the drag and drop instance, so typical
18486          * usage is:
18487          * <code>
18488          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18489          * </code>
18490          * Alternatively:
18491          * <code>
18492          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18493          * </code>
18494          * @TODO this really should be an indexed array.  Alternatively this
18495          * method could accept both.
18496          * @method refreshCache
18497          * @param {Object} groups an associative array of groups to refresh
18498          * @static
18499          */
18500         refreshCache: function(groups) {
18501             for (var sGroup in groups) {
18502                 if ("string" != typeof sGroup) {
18503                     continue;
18504                 }
18505                 for (var i in this.ids[sGroup]) {
18506                     var oDD = this.ids[sGroup][i];
18507
18508                     if (this.isTypeOfDD(oDD)) {
18509                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18510                         var loc = this.getLocation(oDD);
18511                         if (loc) {
18512                             this.locationCache[oDD.id] = loc;
18513                         } else {
18514                             delete this.locationCache[oDD.id];
18515                             // this will unregister the drag and drop object if
18516                             // the element is not in a usable state
18517                             // oDD.unreg();
18518                         }
18519                     }
18520                 }
18521             }
18522         },
18523
18524         /**
18525          * This checks to make sure an element exists and is in the DOM.  The
18526          * main purpose is to handle cases where innerHTML is used to remove
18527          * drag and drop objects from the DOM.  IE provides an 'unspecified
18528          * error' when trying to access the offsetParent of such an element
18529          * @method verifyEl
18530          * @param {HTMLElement} el the element to check
18531          * @return {boolean} true if the element looks usable
18532          * @static
18533          */
18534         verifyEl: function(el) {
18535             if (el) {
18536                 var parent;
18537                 if(Roo.isIE){
18538                     try{
18539                         parent = el.offsetParent;
18540                     }catch(e){}
18541                 }else{
18542                     parent = el.offsetParent;
18543                 }
18544                 if (parent) {
18545                     return true;
18546                 }
18547             }
18548
18549             return false;
18550         },
18551
18552         /**
18553          * Returns a Region object containing the drag and drop element's position
18554          * and size, including the padding configured for it
18555          * @method getLocation
18556          * @param {DragDrop} oDD the drag and drop object to get the
18557          *                       location for
18558          * @return {Roo.lib.Region} a Region object representing the total area
18559          *                             the element occupies, including any padding
18560          *                             the instance is configured for.
18561          * @static
18562          */
18563         getLocation: function(oDD) {
18564             if (! this.isTypeOfDD(oDD)) {
18565                 return null;
18566             }
18567
18568             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18569
18570             try {
18571                 pos= Roo.lib.Dom.getXY(el);
18572             } catch (e) { }
18573
18574             if (!pos) {
18575                 return null;
18576             }
18577
18578             x1 = pos[0];
18579             x2 = x1 + el.offsetWidth;
18580             y1 = pos[1];
18581             y2 = y1 + el.offsetHeight;
18582
18583             t = y1 - oDD.padding[0];
18584             r = x2 + oDD.padding[1];
18585             b = y2 + oDD.padding[2];
18586             l = x1 - oDD.padding[3];
18587
18588             return new Roo.lib.Region( t, r, b, l );
18589         },
18590
18591         /**
18592          * Checks the cursor location to see if it over the target
18593          * @method isOverTarget
18594          * @param {Roo.lib.Point} pt The point to evaluate
18595          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18596          * @return {boolean} true if the mouse is over the target
18597          * @private
18598          * @static
18599          */
18600         isOverTarget: function(pt, oTarget, intersect) {
18601             // use cache if available
18602             var loc = this.locationCache[oTarget.id];
18603             if (!loc || !this.useCache) {
18604                 loc = this.getLocation(oTarget);
18605                 this.locationCache[oTarget.id] = loc;
18606
18607             }
18608
18609             if (!loc) {
18610                 return false;
18611             }
18612
18613             oTarget.cursorIsOver = loc.contains( pt );
18614
18615             // DragDrop is using this as a sanity check for the initial mousedown
18616             // in this case we are done.  In POINT mode, if the drag obj has no
18617             // contraints, we are also done. Otherwise we need to evaluate the
18618             // location of the target as related to the actual location of the
18619             // dragged element.
18620             var dc = this.dragCurrent;
18621             if (!dc || !dc.getTargetCoord ||
18622                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18623                 return oTarget.cursorIsOver;
18624             }
18625
18626             oTarget.overlap = null;
18627
18628             // Get the current location of the drag element, this is the
18629             // location of the mouse event less the delta that represents
18630             // where the original mousedown happened on the element.  We
18631             // need to consider constraints and ticks as well.
18632             var pos = dc.getTargetCoord(pt.x, pt.y);
18633
18634             var el = dc.getDragEl();
18635             var curRegion = new Roo.lib.Region( pos.y,
18636                                                    pos.x + el.offsetWidth,
18637                                                    pos.y + el.offsetHeight,
18638                                                    pos.x );
18639
18640             var overlap = curRegion.intersect(loc);
18641
18642             if (overlap) {
18643                 oTarget.overlap = overlap;
18644                 return (intersect) ? true : oTarget.cursorIsOver;
18645             } else {
18646                 return false;
18647             }
18648         },
18649
18650         /**
18651          * unload event handler
18652          * @method _onUnload
18653          * @private
18654          * @static
18655          */
18656         _onUnload: function(e, me) {
18657             Roo.dd.DragDropMgr.unregAll();
18658         },
18659
18660         /**
18661          * Cleans up the drag and drop events and objects.
18662          * @method unregAll
18663          * @private
18664          * @static
18665          */
18666         unregAll: function() {
18667
18668             if (this.dragCurrent) {
18669                 this.stopDrag();
18670                 this.dragCurrent = null;
18671             }
18672
18673             this._execOnAll("unreg", []);
18674
18675             for (i in this.elementCache) {
18676                 delete this.elementCache[i];
18677             }
18678
18679             this.elementCache = {};
18680             this.ids = {};
18681         },
18682
18683         /**
18684          * A cache of DOM elements
18685          * @property elementCache
18686          * @private
18687          * @static
18688          */
18689         elementCache: {},
18690
18691         /**
18692          * Get the wrapper for the DOM element specified
18693          * @method getElWrapper
18694          * @param {String} id the id of the element to get
18695          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18696          * @private
18697          * @deprecated This wrapper isn't that useful
18698          * @static
18699          */
18700         getElWrapper: function(id) {
18701             var oWrapper = this.elementCache[id];
18702             if (!oWrapper || !oWrapper.el) {
18703                 oWrapper = this.elementCache[id] =
18704                     new this.ElementWrapper(Roo.getDom(id));
18705             }
18706             return oWrapper;
18707         },
18708
18709         /**
18710          * Returns the actual DOM element
18711          * @method getElement
18712          * @param {String} id the id of the elment to get
18713          * @return {Object} The element
18714          * @deprecated use Roo.getDom instead
18715          * @static
18716          */
18717         getElement: function(id) {
18718             return Roo.getDom(id);
18719         },
18720
18721         /**
18722          * Returns the style property for the DOM element (i.e.,
18723          * document.getElById(id).style)
18724          * @method getCss
18725          * @param {String} id the id of the elment to get
18726          * @return {Object} The style property of the element
18727          * @deprecated use Roo.getDom instead
18728          * @static
18729          */
18730         getCss: function(id) {
18731             var el = Roo.getDom(id);
18732             return (el) ? el.style : null;
18733         },
18734
18735         /**
18736          * Inner class for cached elements
18737          * @class DragDropMgr.ElementWrapper
18738          * @for DragDropMgr
18739          * @private
18740          * @deprecated
18741          */
18742         ElementWrapper: function(el) {
18743                 /**
18744                  * The element
18745                  * @property el
18746                  */
18747                 this.el = el || null;
18748                 /**
18749                  * The element id
18750                  * @property id
18751                  */
18752                 this.id = this.el && el.id;
18753                 /**
18754                  * A reference to the style property
18755                  * @property css
18756                  */
18757                 this.css = this.el && el.style;
18758             },
18759
18760         /**
18761          * Returns the X position of an html element
18762          * @method getPosX
18763          * @param el the element for which to get the position
18764          * @return {int} the X coordinate
18765          * @for DragDropMgr
18766          * @deprecated use Roo.lib.Dom.getX instead
18767          * @static
18768          */
18769         getPosX: function(el) {
18770             return Roo.lib.Dom.getX(el);
18771         },
18772
18773         /**
18774          * Returns the Y position of an html element
18775          * @method getPosY
18776          * @param el the element for which to get the position
18777          * @return {int} the Y coordinate
18778          * @deprecated use Roo.lib.Dom.getY instead
18779          * @static
18780          */
18781         getPosY: function(el) {
18782             return Roo.lib.Dom.getY(el);
18783         },
18784
18785         /**
18786          * Swap two nodes.  In IE, we use the native method, for others we
18787          * emulate the IE behavior
18788          * @method swapNode
18789          * @param n1 the first node to swap
18790          * @param n2 the other node to swap
18791          * @static
18792          */
18793         swapNode: function(n1, n2) {
18794             if (n1.swapNode) {
18795                 n1.swapNode(n2);
18796             } else {
18797                 var p = n2.parentNode;
18798                 var s = n2.nextSibling;
18799
18800                 if (s == n1) {
18801                     p.insertBefore(n1, n2);
18802                 } else if (n2 == n1.nextSibling) {
18803                     p.insertBefore(n2, n1);
18804                 } else {
18805                     n1.parentNode.replaceChild(n2, n1);
18806                     p.insertBefore(n1, s);
18807                 }
18808             }
18809         },
18810
18811         /**
18812          * Returns the current scroll position
18813          * @method getScroll
18814          * @private
18815          * @static
18816          */
18817         getScroll: function () {
18818             var t, l, dde=document.documentElement, db=document.body;
18819             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18820                 t = dde.scrollTop;
18821                 l = dde.scrollLeft;
18822             } else if (db) {
18823                 t = db.scrollTop;
18824                 l = db.scrollLeft;
18825             } else {
18826
18827             }
18828             return { top: t, left: l };
18829         },
18830
18831         /**
18832          * Returns the specified element style property
18833          * @method getStyle
18834          * @param {HTMLElement} el          the element
18835          * @param {string}      styleProp   the style property
18836          * @return {string} The value of the style property
18837          * @deprecated use Roo.lib.Dom.getStyle
18838          * @static
18839          */
18840         getStyle: function(el, styleProp) {
18841             return Roo.fly(el).getStyle(styleProp);
18842         },
18843
18844         /**
18845          * Gets the scrollTop
18846          * @method getScrollTop
18847          * @return {int} the document's scrollTop
18848          * @static
18849          */
18850         getScrollTop: function () { return this.getScroll().top; },
18851
18852         /**
18853          * Gets the scrollLeft
18854          * @method getScrollLeft
18855          * @return {int} the document's scrollTop
18856          * @static
18857          */
18858         getScrollLeft: function () { return this.getScroll().left; },
18859
18860         /**
18861          * Sets the x/y position of an element to the location of the
18862          * target element.
18863          * @method moveToEl
18864          * @param {HTMLElement} moveEl      The element to move
18865          * @param {HTMLElement} targetEl    The position reference element
18866          * @static
18867          */
18868         moveToEl: function (moveEl, targetEl) {
18869             var aCoord = Roo.lib.Dom.getXY(targetEl);
18870             Roo.lib.Dom.setXY(moveEl, aCoord);
18871         },
18872
18873         /**
18874          * Numeric array sort function
18875          * @method numericSort
18876          * @static
18877          */
18878         numericSort: function(a, b) { return (a - b); },
18879
18880         /**
18881          * Internal counter
18882          * @property _timeoutCount
18883          * @private
18884          * @static
18885          */
18886         _timeoutCount: 0,
18887
18888         /**
18889          * Trying to make the load order less important.  Without this we get
18890          * an error if this file is loaded before the Event Utility.
18891          * @method _addListeners
18892          * @private
18893          * @static
18894          */
18895         _addListeners: function() {
18896             var DDM = Roo.dd.DDM;
18897             if ( Roo.lib.Event && document ) {
18898                 DDM._onLoad();
18899             } else {
18900                 if (DDM._timeoutCount > 2000) {
18901                 } else {
18902                     setTimeout(DDM._addListeners, 10);
18903                     if (document && document.body) {
18904                         DDM._timeoutCount += 1;
18905                     }
18906                 }
18907             }
18908         },
18909
18910         /**
18911          * Recursively searches the immediate parent and all child nodes for
18912          * the handle element in order to determine wheter or not it was
18913          * clicked.
18914          * @method handleWasClicked
18915          * @param node the html element to inspect
18916          * @static
18917          */
18918         handleWasClicked: function(node, id) {
18919             if (this.isHandle(id, node.id)) {
18920                 return true;
18921             } else {
18922                 // check to see if this is a text node child of the one we want
18923                 var p = node.parentNode;
18924
18925                 while (p) {
18926                     if (this.isHandle(id, p.id)) {
18927                         return true;
18928                     } else {
18929                         p = p.parentNode;
18930                     }
18931                 }
18932             }
18933
18934             return false;
18935         }
18936
18937     };
18938
18939 }();
18940
18941 // shorter alias, save a few bytes
18942 Roo.dd.DDM = Roo.dd.DragDropMgr;
18943 Roo.dd.DDM._addListeners();
18944
18945 }/*
18946  * Based on:
18947  * Ext JS Library 1.1.1
18948  * Copyright(c) 2006-2007, Ext JS, LLC.
18949  *
18950  * Originally Released Under LGPL - original licence link has changed is not relivant.
18951  *
18952  * Fork - LGPL
18953  * <script type="text/javascript">
18954  */
18955
18956 /**
18957  * @class Roo.dd.DD
18958  * A DragDrop implementation where the linked element follows the
18959  * mouse cursor during a drag.
18960  * @extends Roo.dd.DragDrop
18961  * @constructor
18962  * @param {String} id the id of the linked element
18963  * @param {String} sGroup the group of related DragDrop items
18964  * @param {object} config an object containing configurable attributes
18965  *                Valid properties for DD:
18966  *                    scroll
18967  */
18968 Roo.dd.DD = function(id, sGroup, config) {
18969     if (id) {
18970         this.init(id, sGroup, config);
18971     }
18972 };
18973
18974 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18975
18976     /**
18977      * When set to true, the utility automatically tries to scroll the browser
18978      * window wehn a drag and drop element is dragged near the viewport boundary.
18979      * Defaults to true.
18980      * @property scroll
18981      * @type boolean
18982      */
18983     scroll: true,
18984
18985     /**
18986      * Sets the pointer offset to the distance between the linked element's top
18987      * left corner and the location the element was clicked
18988      * @method autoOffset
18989      * @param {int} iPageX the X coordinate of the click
18990      * @param {int} iPageY the Y coordinate of the click
18991      */
18992     autoOffset: function(iPageX, iPageY) {
18993         var x = iPageX - this.startPageX;
18994         var y = iPageY - this.startPageY;
18995         this.setDelta(x, y);
18996     },
18997
18998     /**
18999      * Sets the pointer offset.  You can call this directly to force the
19000      * offset to be in a particular location (e.g., pass in 0,0 to set it
19001      * to the center of the object)
19002      * @method setDelta
19003      * @param {int} iDeltaX the distance from the left
19004      * @param {int} iDeltaY the distance from the top
19005      */
19006     setDelta: function(iDeltaX, iDeltaY) {
19007         this.deltaX = iDeltaX;
19008         this.deltaY = iDeltaY;
19009     },
19010
19011     /**
19012      * Sets the drag element to the location of the mousedown or click event,
19013      * maintaining the cursor location relative to the location on the element
19014      * that was clicked.  Override this if you want to place the element in a
19015      * location other than where the cursor is.
19016      * @method setDragElPos
19017      * @param {int} iPageX the X coordinate of the mousedown or drag event
19018      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19019      */
19020     setDragElPos: function(iPageX, iPageY) {
19021         // the first time we do this, we are going to check to make sure
19022         // the element has css positioning
19023
19024         var el = this.getDragEl();
19025         this.alignElWithMouse(el, iPageX, iPageY);
19026     },
19027
19028     /**
19029      * Sets the element to the location of the mousedown or click event,
19030      * maintaining the cursor location relative to the location on the element
19031      * that was clicked.  Override this if you want to place the element in a
19032      * location other than where the cursor is.
19033      * @method alignElWithMouse
19034      * @param {HTMLElement} el the element to move
19035      * @param {int} iPageX the X coordinate of the mousedown or drag event
19036      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19037      */
19038     alignElWithMouse: function(el, iPageX, iPageY) {
19039         var oCoord = this.getTargetCoord(iPageX, iPageY);
19040         var fly = el.dom ? el : Roo.fly(el);
19041         if (!this.deltaSetXY) {
19042             var aCoord = [oCoord.x, oCoord.y];
19043             fly.setXY(aCoord);
19044             var newLeft = fly.getLeft(true);
19045             var newTop  = fly.getTop(true);
19046             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19047         } else {
19048             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19049         }
19050
19051         this.cachePosition(oCoord.x, oCoord.y);
19052         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19053         return oCoord;
19054     },
19055
19056     /**
19057      * Saves the most recent position so that we can reset the constraints and
19058      * tick marks on-demand.  We need to know this so that we can calculate the
19059      * number of pixels the element is offset from its original position.
19060      * @method cachePosition
19061      * @param iPageX the current x position (optional, this just makes it so we
19062      * don't have to look it up again)
19063      * @param iPageY the current y position (optional, this just makes it so we
19064      * don't have to look it up again)
19065      */
19066     cachePosition: function(iPageX, iPageY) {
19067         if (iPageX) {
19068             this.lastPageX = iPageX;
19069             this.lastPageY = iPageY;
19070         } else {
19071             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19072             this.lastPageX = aCoord[0];
19073             this.lastPageY = aCoord[1];
19074         }
19075     },
19076
19077     /**
19078      * Auto-scroll the window if the dragged object has been moved beyond the
19079      * visible window boundary.
19080      * @method autoScroll
19081      * @param {int} x the drag element's x position
19082      * @param {int} y the drag element's y position
19083      * @param {int} h the height of the drag element
19084      * @param {int} w the width of the drag element
19085      * @private
19086      */
19087     autoScroll: function(x, y, h, w) {
19088
19089         if (this.scroll) {
19090             // The client height
19091             var clientH = Roo.lib.Dom.getViewWidth();
19092
19093             // The client width
19094             var clientW = Roo.lib.Dom.getViewHeight();
19095
19096             // The amt scrolled down
19097             var st = this.DDM.getScrollTop();
19098
19099             // The amt scrolled right
19100             var sl = this.DDM.getScrollLeft();
19101
19102             // Location of the bottom of the element
19103             var bot = h + y;
19104
19105             // Location of the right of the element
19106             var right = w + x;
19107
19108             // The distance from the cursor to the bottom of the visible area,
19109             // adjusted so that we don't scroll if the cursor is beyond the
19110             // element drag constraints
19111             var toBot = (clientH + st - y - this.deltaY);
19112
19113             // The distance from the cursor to the right of the visible area
19114             var toRight = (clientW + sl - x - this.deltaX);
19115
19116
19117             // How close to the edge the cursor must be before we scroll
19118             // var thresh = (document.all) ? 100 : 40;
19119             var thresh = 40;
19120
19121             // How many pixels to scroll per autoscroll op.  This helps to reduce
19122             // clunky scrolling. IE is more sensitive about this ... it needs this
19123             // value to be higher.
19124             var scrAmt = (document.all) ? 80 : 30;
19125
19126             // Scroll down if we are near the bottom of the visible page and the
19127             // obj extends below the crease
19128             if ( bot > clientH && toBot < thresh ) {
19129                 window.scrollTo(sl, st + scrAmt);
19130             }
19131
19132             // Scroll up if the window is scrolled down and the top of the object
19133             // goes above the top border
19134             if ( y < st && st > 0 && y - st < thresh ) {
19135                 window.scrollTo(sl, st - scrAmt);
19136             }
19137
19138             // Scroll right if the obj is beyond the right border and the cursor is
19139             // near the border.
19140             if ( right > clientW && toRight < thresh ) {
19141                 window.scrollTo(sl + scrAmt, st);
19142             }
19143
19144             // Scroll left if the window has been scrolled to the right and the obj
19145             // extends past the left border
19146             if ( x < sl && sl > 0 && x - sl < thresh ) {
19147                 window.scrollTo(sl - scrAmt, st);
19148             }
19149         }
19150     },
19151
19152     /**
19153      * Finds the location the element should be placed if we want to move
19154      * it to where the mouse location less the click offset would place us.
19155      * @method getTargetCoord
19156      * @param {int} iPageX the X coordinate of the click
19157      * @param {int} iPageY the Y coordinate of the click
19158      * @return an object that contains the coordinates (Object.x and Object.y)
19159      * @private
19160      */
19161     getTargetCoord: function(iPageX, iPageY) {
19162
19163
19164         var x = iPageX - this.deltaX;
19165         var y = iPageY - this.deltaY;
19166
19167         if (this.constrainX) {
19168             if (x < this.minX) { x = this.minX; }
19169             if (x > this.maxX) { x = this.maxX; }
19170         }
19171
19172         if (this.constrainY) {
19173             if (y < this.minY) { y = this.minY; }
19174             if (y > this.maxY) { y = this.maxY; }
19175         }
19176
19177         x = this.getTick(x, this.xTicks);
19178         y = this.getTick(y, this.yTicks);
19179
19180
19181         return {x:x, y:y};
19182     },
19183
19184     /*
19185      * Sets up config options specific to this class. Overrides
19186      * Roo.dd.DragDrop, but all versions of this method through the
19187      * inheritance chain are called
19188      */
19189     applyConfig: function() {
19190         Roo.dd.DD.superclass.applyConfig.call(this);
19191         this.scroll = (this.config.scroll !== false);
19192     },
19193
19194     /*
19195      * Event that fires prior to the onMouseDown event.  Overrides
19196      * Roo.dd.DragDrop.
19197      */
19198     b4MouseDown: function(e) {
19199         // this.resetConstraints();
19200         this.autoOffset(e.getPageX(),
19201                             e.getPageY());
19202     },
19203
19204     /*
19205      * Event that fires prior to the onDrag event.  Overrides
19206      * Roo.dd.DragDrop.
19207      */
19208     b4Drag: function(e) {
19209         this.setDragElPos(e.getPageX(),
19210                             e.getPageY());
19211     },
19212
19213     toString: function() {
19214         return ("DD " + this.id);
19215     }
19216
19217     //////////////////////////////////////////////////////////////////////////
19218     // Debugging ygDragDrop events that can be overridden
19219     //////////////////////////////////////////////////////////////////////////
19220     /*
19221     startDrag: function(x, y) {
19222     },
19223
19224     onDrag: function(e) {
19225     },
19226
19227     onDragEnter: function(e, id) {
19228     },
19229
19230     onDragOver: function(e, id) {
19231     },
19232
19233     onDragOut: function(e, id) {
19234     },
19235
19236     onDragDrop: function(e, id) {
19237     },
19238
19239     endDrag: function(e) {
19240     }
19241
19242     */
19243
19244 });/*
19245  * Based on:
19246  * Ext JS Library 1.1.1
19247  * Copyright(c) 2006-2007, Ext JS, LLC.
19248  *
19249  * Originally Released Under LGPL - original licence link has changed is not relivant.
19250  *
19251  * Fork - LGPL
19252  * <script type="text/javascript">
19253  */
19254
19255 /**
19256  * @class Roo.dd.DDProxy
19257  * A DragDrop implementation that inserts an empty, bordered div into
19258  * the document that follows the cursor during drag operations.  At the time of
19259  * the click, the frame div is resized to the dimensions of the linked html
19260  * element, and moved to the exact location of the linked element.
19261  *
19262  * References to the "frame" element refer to the single proxy element that
19263  * was created to be dragged in place of all DDProxy elements on the
19264  * page.
19265  *
19266  * @extends Roo.dd.DD
19267  * @constructor
19268  * @param {String} id the id of the linked html element
19269  * @param {String} sGroup the group of related DragDrop objects
19270  * @param {object} config an object containing configurable attributes
19271  *                Valid properties for DDProxy in addition to those in DragDrop:
19272  *                   resizeFrame, centerFrame, dragElId
19273  */
19274 Roo.dd.DDProxy = function(id, sGroup, config) {
19275     if (id) {
19276         this.init(id, sGroup, config);
19277         this.initFrame();
19278     }
19279 };
19280
19281 /**
19282  * The default drag frame div id
19283  * @property Roo.dd.DDProxy.dragElId
19284  * @type String
19285  * @static
19286  */
19287 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19288
19289 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19290
19291     /**
19292      * By default we resize the drag frame to be the same size as the element
19293      * we want to drag (this is to get the frame effect).  We can turn it off
19294      * if we want a different behavior.
19295      * @property resizeFrame
19296      * @type boolean
19297      */
19298     resizeFrame: true,
19299
19300     /**
19301      * By default the frame is positioned exactly where the drag element is, so
19302      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19303      * you do not have constraints on the obj is to have the drag frame centered
19304      * around the cursor.  Set centerFrame to true for this effect.
19305      * @property centerFrame
19306      * @type boolean
19307      */
19308     centerFrame: false,
19309
19310     /**
19311      * Creates the proxy element if it does not yet exist
19312      * @method createFrame
19313      */
19314     createFrame: function() {
19315         var self = this;
19316         var body = document.body;
19317
19318         if (!body || !body.firstChild) {
19319             setTimeout( function() { self.createFrame(); }, 50 );
19320             return;
19321         }
19322
19323         var div = this.getDragEl();
19324
19325         if (!div) {
19326             div    = document.createElement("div");
19327             div.id = this.dragElId;
19328             var s  = div.style;
19329
19330             s.position   = "absolute";
19331             s.visibility = "hidden";
19332             s.cursor     = "move";
19333             s.border     = "2px solid #aaa";
19334             s.zIndex     = 999;
19335
19336             // appendChild can blow up IE if invoked prior to the window load event
19337             // while rendering a table.  It is possible there are other scenarios
19338             // that would cause this to happen as well.
19339             body.insertBefore(div, body.firstChild);
19340         }
19341     },
19342
19343     /**
19344      * Initialization for the drag frame element.  Must be called in the
19345      * constructor of all subclasses
19346      * @method initFrame
19347      */
19348     initFrame: function() {
19349         this.createFrame();
19350     },
19351
19352     applyConfig: function() {
19353         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19354
19355         this.resizeFrame = (this.config.resizeFrame !== false);
19356         this.centerFrame = (this.config.centerFrame);
19357         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19358     },
19359
19360     /**
19361      * Resizes the drag frame to the dimensions of the clicked object, positions
19362      * it over the object, and finally displays it
19363      * @method showFrame
19364      * @param {int} iPageX X click position
19365      * @param {int} iPageY Y click position
19366      * @private
19367      */
19368     showFrame: function(iPageX, iPageY) {
19369         var el = this.getEl();
19370         var dragEl = this.getDragEl();
19371         var s = dragEl.style;
19372
19373         this._resizeProxy();
19374
19375         if (this.centerFrame) {
19376             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19377                            Math.round(parseInt(s.height, 10)/2) );
19378         }
19379
19380         this.setDragElPos(iPageX, iPageY);
19381
19382         Roo.fly(dragEl).show();
19383     },
19384
19385     /**
19386      * The proxy is automatically resized to the dimensions of the linked
19387      * element when a drag is initiated, unless resizeFrame is set to false
19388      * @method _resizeProxy
19389      * @private
19390      */
19391     _resizeProxy: function() {
19392         if (this.resizeFrame) {
19393             var el = this.getEl();
19394             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19395         }
19396     },
19397
19398     // overrides Roo.dd.DragDrop
19399     b4MouseDown: function(e) {
19400         var x = e.getPageX();
19401         var y = e.getPageY();
19402         this.autoOffset(x, y);
19403         this.setDragElPos(x, y);
19404     },
19405
19406     // overrides Roo.dd.DragDrop
19407     b4StartDrag: function(x, y) {
19408         // show the drag frame
19409         this.showFrame(x, y);
19410     },
19411
19412     // overrides Roo.dd.DragDrop
19413     b4EndDrag: function(e) {
19414         Roo.fly(this.getDragEl()).hide();
19415     },
19416
19417     // overrides Roo.dd.DragDrop
19418     // By default we try to move the element to the last location of the frame.
19419     // This is so that the default behavior mirrors that of Roo.dd.DD.
19420     endDrag: function(e) {
19421
19422         var lel = this.getEl();
19423         var del = this.getDragEl();
19424
19425         // Show the drag frame briefly so we can get its position
19426         del.style.visibility = "";
19427
19428         this.beforeMove();
19429         // Hide the linked element before the move to get around a Safari
19430         // rendering bug.
19431         lel.style.visibility = "hidden";
19432         Roo.dd.DDM.moveToEl(lel, del);
19433         del.style.visibility = "hidden";
19434         lel.style.visibility = "";
19435
19436         this.afterDrag();
19437     },
19438
19439     beforeMove : function(){
19440
19441     },
19442
19443     afterDrag : function(){
19444
19445     },
19446
19447     toString: function() {
19448         return ("DDProxy " + this.id);
19449     }
19450
19451 });
19452 /*
19453  * Based on:
19454  * Ext JS Library 1.1.1
19455  * Copyright(c) 2006-2007, Ext JS, LLC.
19456  *
19457  * Originally Released Under LGPL - original licence link has changed is not relivant.
19458  *
19459  * Fork - LGPL
19460  * <script type="text/javascript">
19461  */
19462
19463  /**
19464  * @class Roo.dd.DDTarget
19465  * A DragDrop implementation that does not move, but can be a drop
19466  * target.  You would get the same result by simply omitting implementation
19467  * for the event callbacks, but this way we reduce the processing cost of the
19468  * event listener and the callbacks.
19469  * @extends Roo.dd.DragDrop
19470  * @constructor
19471  * @param {String} id the id of the element that is a drop target
19472  * @param {String} sGroup the group of related DragDrop objects
19473  * @param {object} config an object containing configurable attributes
19474  *                 Valid properties for DDTarget in addition to those in
19475  *                 DragDrop:
19476  *                    none
19477  */
19478 Roo.dd.DDTarget = function(id, sGroup, config) {
19479     if (id) {
19480         this.initTarget(id, sGroup, config);
19481     }
19482     if (config.listeners || config.events) { 
19483        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19484             listeners : config.listeners || {}, 
19485             events : config.events || {} 
19486         });    
19487     }
19488 };
19489
19490 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19491 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19492     toString: function() {
19493         return ("DDTarget " + this.id);
19494     }
19495 });
19496 /*
19497  * Based on:
19498  * Ext JS Library 1.1.1
19499  * Copyright(c) 2006-2007, Ext JS, LLC.
19500  *
19501  * Originally Released Under LGPL - original licence link has changed is not relivant.
19502  *
19503  * Fork - LGPL
19504  * <script type="text/javascript">
19505  */
19506  
19507
19508 /**
19509  * @class Roo.dd.ScrollManager
19510  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19511  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19512  * @singleton
19513  */
19514 Roo.dd.ScrollManager = function(){
19515     var ddm = Roo.dd.DragDropMgr;
19516     var els = {};
19517     var dragEl = null;
19518     var proc = {};
19519     
19520     
19521     
19522     var onStop = function(e){
19523         dragEl = null;
19524         clearProc();
19525     };
19526     
19527     var triggerRefresh = function(){
19528         if(ddm.dragCurrent){
19529              ddm.refreshCache(ddm.dragCurrent.groups);
19530         }
19531     };
19532     
19533     var doScroll = function(){
19534         if(ddm.dragCurrent){
19535             var dds = Roo.dd.ScrollManager;
19536             if(!dds.animate){
19537                 if(proc.el.scroll(proc.dir, dds.increment)){
19538                     triggerRefresh();
19539                 }
19540             }else{
19541                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19542             }
19543         }
19544     };
19545     
19546     var clearProc = function(){
19547         if(proc.id){
19548             clearInterval(proc.id);
19549         }
19550         proc.id = 0;
19551         proc.el = null;
19552         proc.dir = "";
19553     };
19554     
19555     var startProc = function(el, dir){
19556          Roo.log('scroll startproc');
19557         clearProc();
19558         proc.el = el;
19559         proc.dir = dir;
19560         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19561     };
19562     
19563     var onFire = function(e, isDrop){
19564        
19565         if(isDrop || !ddm.dragCurrent){ return; }
19566         var dds = Roo.dd.ScrollManager;
19567         if(!dragEl || dragEl != ddm.dragCurrent){
19568             dragEl = ddm.dragCurrent;
19569             // refresh regions on drag start
19570             dds.refreshCache();
19571         }
19572         
19573         var xy = Roo.lib.Event.getXY(e);
19574         var pt = new Roo.lib.Point(xy[0], xy[1]);
19575         for(var id in els){
19576             var el = els[id], r = el._region;
19577             if(r && r.contains(pt) && el.isScrollable()){
19578                 if(r.bottom - pt.y <= dds.thresh){
19579                     if(proc.el != el){
19580                         startProc(el, "down");
19581                     }
19582                     return;
19583                 }else if(r.right - pt.x <= dds.thresh){
19584                     if(proc.el != el){
19585                         startProc(el, "left");
19586                     }
19587                     return;
19588                 }else if(pt.y - r.top <= dds.thresh){
19589                     if(proc.el != el){
19590                         startProc(el, "up");
19591                     }
19592                     return;
19593                 }else if(pt.x - r.left <= dds.thresh){
19594                     if(proc.el != el){
19595                         startProc(el, "right");
19596                     }
19597                     return;
19598                 }
19599             }
19600         }
19601         clearProc();
19602     };
19603     
19604     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19605     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19606     
19607     return {
19608         /**
19609          * Registers new overflow element(s) to auto scroll
19610          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19611          */
19612         register : function(el){
19613             if(el instanceof Array){
19614                 for(var i = 0, len = el.length; i < len; i++) {
19615                         this.register(el[i]);
19616                 }
19617             }else{
19618                 el = Roo.get(el);
19619                 els[el.id] = el;
19620             }
19621             Roo.dd.ScrollManager.els = els;
19622         },
19623         
19624         /**
19625          * Unregisters overflow element(s) so they are no longer scrolled
19626          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19627          */
19628         unregister : function(el){
19629             if(el instanceof Array){
19630                 for(var i = 0, len = el.length; i < len; i++) {
19631                         this.unregister(el[i]);
19632                 }
19633             }else{
19634                 el = Roo.get(el);
19635                 delete els[el.id];
19636             }
19637         },
19638         
19639         /**
19640          * The number of pixels from the edge of a container the pointer needs to be to 
19641          * trigger scrolling (defaults to 25)
19642          * @type Number
19643          */
19644         thresh : 25,
19645         
19646         /**
19647          * The number of pixels to scroll in each scroll increment (defaults to 50)
19648          * @type Number
19649          */
19650         increment : 100,
19651         
19652         /**
19653          * The frequency of scrolls in milliseconds (defaults to 500)
19654          * @type Number
19655          */
19656         frequency : 500,
19657         
19658         /**
19659          * True to animate the scroll (defaults to true)
19660          * @type Boolean
19661          */
19662         animate: true,
19663         
19664         /**
19665          * The animation duration in seconds - 
19666          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19667          * @type Number
19668          */
19669         animDuration: .4,
19670         
19671         /**
19672          * Manually trigger a cache refresh.
19673          */
19674         refreshCache : function(){
19675             for(var id in els){
19676                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19677                     els[id]._region = els[id].getRegion();
19678                 }
19679             }
19680         }
19681     };
19682 }();/*
19683  * Based on:
19684  * Ext JS Library 1.1.1
19685  * Copyright(c) 2006-2007, Ext JS, LLC.
19686  *
19687  * Originally Released Under LGPL - original licence link has changed is not relivant.
19688  *
19689  * Fork - LGPL
19690  * <script type="text/javascript">
19691  */
19692  
19693
19694 /**
19695  * @class Roo.dd.Registry
19696  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19697  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19698  * @singleton
19699  */
19700 Roo.dd.Registry = function(){
19701     var elements = {}; 
19702     var handles = {}; 
19703     var autoIdSeed = 0;
19704
19705     var getId = function(el, autogen){
19706         if(typeof el == "string"){
19707             return el;
19708         }
19709         var id = el.id;
19710         if(!id && autogen !== false){
19711             id = "roodd-" + (++autoIdSeed);
19712             el.id = id;
19713         }
19714         return id;
19715     };
19716     
19717     return {
19718     /**
19719      * Register a drag drop element
19720      * @param {String|HTMLElement} element The id or DOM node to register
19721      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19722      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19723      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19724      * populated in the data object (if applicable):
19725      * <pre>
19726 Value      Description<br />
19727 ---------  ------------------------------------------<br />
19728 handles    Array of DOM nodes that trigger dragging<br />
19729            for the element being registered<br />
19730 isHandle   True if the element passed in triggers<br />
19731            dragging itself, else false
19732 </pre>
19733      */
19734         register : function(el, data){
19735             data = data || {};
19736             if(typeof el == "string"){
19737                 el = document.getElementById(el);
19738             }
19739             data.ddel = el;
19740             elements[getId(el)] = data;
19741             if(data.isHandle !== false){
19742                 handles[data.ddel.id] = data;
19743             }
19744             if(data.handles){
19745                 var hs = data.handles;
19746                 for(var i = 0, len = hs.length; i < len; i++){
19747                         handles[getId(hs[i])] = data;
19748                 }
19749             }
19750         },
19751
19752     /**
19753      * Unregister a drag drop element
19754      * @param {String|HTMLElement}  element The id or DOM node to unregister
19755      */
19756         unregister : function(el){
19757             var id = getId(el, false);
19758             var data = elements[id];
19759             if(data){
19760                 delete elements[id];
19761                 if(data.handles){
19762                     var hs = data.handles;
19763                     for(var i = 0, len = hs.length; i < len; i++){
19764                         delete handles[getId(hs[i], false)];
19765                     }
19766                 }
19767             }
19768         },
19769
19770     /**
19771      * Returns the handle registered for a DOM Node by id
19772      * @param {String|HTMLElement} id The DOM node or id to look up
19773      * @return {Object} handle The custom handle data
19774      */
19775         getHandle : function(id){
19776             if(typeof id != "string"){ // must be element?
19777                 id = id.id;
19778             }
19779             return handles[id];
19780         },
19781
19782     /**
19783      * Returns the handle that is registered for the DOM node that is the target of the event
19784      * @param {Event} e The event
19785      * @return {Object} handle The custom handle data
19786      */
19787         getHandleFromEvent : function(e){
19788             var t = Roo.lib.Event.getTarget(e);
19789             return t ? handles[t.id] : null;
19790         },
19791
19792     /**
19793      * Returns a custom data object that is registered for a DOM node by id
19794      * @param {String|HTMLElement} id The DOM node or id to look up
19795      * @return {Object} data The custom data
19796      */
19797         getTarget : function(id){
19798             if(typeof id != "string"){ // must be element?
19799                 id = id.id;
19800             }
19801             return elements[id];
19802         },
19803
19804     /**
19805      * Returns a custom data object that is registered for the DOM node that is the target of the event
19806      * @param {Event} e The event
19807      * @return {Object} data The custom data
19808      */
19809         getTargetFromEvent : function(e){
19810             var t = Roo.lib.Event.getTarget(e);
19811             return t ? elements[t.id] || handles[t.id] : null;
19812         }
19813     };
19814 }();/*
19815  * Based on:
19816  * Ext JS Library 1.1.1
19817  * Copyright(c) 2006-2007, Ext JS, LLC.
19818  *
19819  * Originally Released Under LGPL - original licence link has changed is not relivant.
19820  *
19821  * Fork - LGPL
19822  * <script type="text/javascript">
19823  */
19824  
19825
19826 /**
19827  * @class Roo.dd.StatusProxy
19828  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19829  * default drag proxy used by all Roo.dd components.
19830  * @constructor
19831  * @param {Object} config
19832  */
19833 Roo.dd.StatusProxy = function(config){
19834     Roo.apply(this, config);
19835     this.id = this.id || Roo.id();
19836     this.el = new Roo.Layer({
19837         dh: {
19838             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19839                 {tag: "div", cls: "x-dd-drop-icon"},
19840                 {tag: "div", cls: "x-dd-drag-ghost"}
19841             ]
19842         }, 
19843         shadow: !config || config.shadow !== false
19844     });
19845     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19846     this.dropStatus = this.dropNotAllowed;
19847 };
19848
19849 Roo.dd.StatusProxy.prototype = {
19850     /**
19851      * @cfg {String} dropAllowed
19852      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19853      */
19854     dropAllowed : "x-dd-drop-ok",
19855     /**
19856      * @cfg {String} dropNotAllowed
19857      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19858      */
19859     dropNotAllowed : "x-dd-drop-nodrop",
19860
19861     /**
19862      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19863      * over the current target element.
19864      * @param {String} cssClass The css class for the new drop status indicator image
19865      */
19866     setStatus : function(cssClass){
19867         cssClass = cssClass || this.dropNotAllowed;
19868         if(this.dropStatus != cssClass){
19869             this.el.replaceClass(this.dropStatus, cssClass);
19870             this.dropStatus = cssClass;
19871         }
19872     },
19873
19874     /**
19875      * Resets the status indicator to the default dropNotAllowed value
19876      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19877      */
19878     reset : function(clearGhost){
19879         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19880         this.dropStatus = this.dropNotAllowed;
19881         if(clearGhost){
19882             this.ghost.update("");
19883         }
19884     },
19885
19886     /**
19887      * Updates the contents of the ghost element
19888      * @param {String} html The html that will replace the current innerHTML of the ghost element
19889      */
19890     update : function(html){
19891         if(typeof html == "string"){
19892             this.ghost.update(html);
19893         }else{
19894             this.ghost.update("");
19895             html.style.margin = "0";
19896             this.ghost.dom.appendChild(html);
19897         }
19898         // ensure float = none set?? cant remember why though.
19899         var el = this.ghost.dom.firstChild;
19900                 if(el){
19901                         Roo.fly(el).setStyle('float', 'none');
19902                 }
19903     },
19904     
19905     /**
19906      * Returns the underlying proxy {@link Roo.Layer}
19907      * @return {Roo.Layer} el
19908     */
19909     getEl : function(){
19910         return this.el;
19911     },
19912
19913     /**
19914      * Returns the ghost element
19915      * @return {Roo.Element} el
19916      */
19917     getGhost : function(){
19918         return this.ghost;
19919     },
19920
19921     /**
19922      * Hides the proxy
19923      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19924      */
19925     hide : function(clear){
19926         this.el.hide();
19927         if(clear){
19928             this.reset(true);
19929         }
19930     },
19931
19932     /**
19933      * Stops the repair animation if it's currently running
19934      */
19935     stop : function(){
19936         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19937             this.anim.stop();
19938         }
19939     },
19940
19941     /**
19942      * Displays this proxy
19943      */
19944     show : function(){
19945         this.el.show();
19946     },
19947
19948     /**
19949      * Force the Layer to sync its shadow and shim positions to the element
19950      */
19951     sync : function(){
19952         this.el.sync();
19953     },
19954
19955     /**
19956      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19957      * invalid drop operation by the item being dragged.
19958      * @param {Array} xy The XY position of the element ([x, y])
19959      * @param {Function} callback The function to call after the repair is complete
19960      * @param {Object} scope The scope in which to execute the callback
19961      */
19962     repair : function(xy, callback, scope){
19963         this.callback = callback;
19964         this.scope = scope;
19965         if(xy && this.animRepair !== false){
19966             this.el.addClass("x-dd-drag-repair");
19967             this.el.hideUnders(true);
19968             this.anim = this.el.shift({
19969                 duration: this.repairDuration || .5,
19970                 easing: 'easeOut',
19971                 xy: xy,
19972                 stopFx: true,
19973                 callback: this.afterRepair,
19974                 scope: this
19975             });
19976         }else{
19977             this.afterRepair();
19978         }
19979     },
19980
19981     // private
19982     afterRepair : function(){
19983         this.hide(true);
19984         if(typeof this.callback == "function"){
19985             this.callback.call(this.scope || this);
19986         }
19987         this.callback = null;
19988         this.scope = null;
19989     }
19990 };/*
19991  * Based on:
19992  * Ext JS Library 1.1.1
19993  * Copyright(c) 2006-2007, Ext JS, LLC.
19994  *
19995  * Originally Released Under LGPL - original licence link has changed is not relivant.
19996  *
19997  * Fork - LGPL
19998  * <script type="text/javascript">
19999  */
20000
20001 /**
20002  * @class Roo.dd.DragSource
20003  * @extends Roo.dd.DDProxy
20004  * A simple class that provides the basic implementation needed to make any element draggable.
20005  * @constructor
20006  * @param {String/HTMLElement/Element} el The container element
20007  * @param {Object} config
20008  */
20009 Roo.dd.DragSource = function(el, config){
20010     this.el = Roo.get(el);
20011     this.dragData = {};
20012     
20013     Roo.apply(this, config);
20014     
20015     if(!this.proxy){
20016         this.proxy = new Roo.dd.StatusProxy();
20017     }
20018
20019     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20020           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20021     
20022     this.dragging = false;
20023 };
20024
20025 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20026     /**
20027      * @cfg {String} dropAllowed
20028      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20029      */
20030     dropAllowed : "x-dd-drop-ok",
20031     /**
20032      * @cfg {String} dropNotAllowed
20033      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20034      */
20035     dropNotAllowed : "x-dd-drop-nodrop",
20036
20037     /**
20038      * Returns the data object associated with this drag source
20039      * @return {Object} data An object containing arbitrary data
20040      */
20041     getDragData : function(e){
20042         return this.dragData;
20043     },
20044
20045     // private
20046     onDragEnter : function(e, id){
20047         var target = Roo.dd.DragDropMgr.getDDById(id);
20048         this.cachedTarget = target;
20049         if(this.beforeDragEnter(target, e, id) !== false){
20050             if(target.isNotifyTarget){
20051                 var status = target.notifyEnter(this, e, this.dragData);
20052                 this.proxy.setStatus(status);
20053             }else{
20054                 this.proxy.setStatus(this.dropAllowed);
20055             }
20056             
20057             if(this.afterDragEnter){
20058                 /**
20059                  * An empty function by default, but provided so that you can perform a custom action
20060                  * when the dragged item enters the drop target by providing an implementation.
20061                  * @param {Roo.dd.DragDrop} target The drop target
20062                  * @param {Event} e The event object
20063                  * @param {String} id The id of the dragged element
20064                  * @method afterDragEnter
20065                  */
20066                 this.afterDragEnter(target, e, id);
20067             }
20068         }
20069     },
20070
20071     /**
20072      * An empty function by default, but provided so that you can perform a custom action
20073      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20074      * @param {Roo.dd.DragDrop} target The drop target
20075      * @param {Event} e The event object
20076      * @param {String} id The id of the dragged element
20077      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20078      */
20079     beforeDragEnter : function(target, e, id){
20080         return true;
20081     },
20082
20083     // private
20084     alignElWithMouse: function() {
20085         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20086         this.proxy.sync();
20087     },
20088
20089     // private
20090     onDragOver : function(e, id){
20091         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20092         if(this.beforeDragOver(target, e, id) !== false){
20093             if(target.isNotifyTarget){
20094                 var status = target.notifyOver(this, e, this.dragData);
20095                 this.proxy.setStatus(status);
20096             }
20097
20098             if(this.afterDragOver){
20099                 /**
20100                  * An empty function by default, but provided so that you can perform a custom action
20101                  * while the dragged item is over the drop target by providing an implementation.
20102                  * @param {Roo.dd.DragDrop} target The drop target
20103                  * @param {Event} e The event object
20104                  * @param {String} id The id of the dragged element
20105                  * @method afterDragOver
20106                  */
20107                 this.afterDragOver(target, e, id);
20108             }
20109         }
20110     },
20111
20112     /**
20113      * An empty function by default, but provided so that you can perform a custom action
20114      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20115      * @param {Roo.dd.DragDrop} target The drop target
20116      * @param {Event} e The event object
20117      * @param {String} id The id of the dragged element
20118      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20119      */
20120     beforeDragOver : function(target, e, id){
20121         return true;
20122     },
20123
20124     // private
20125     onDragOut : function(e, id){
20126         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20127         if(this.beforeDragOut(target, e, id) !== false){
20128             if(target.isNotifyTarget){
20129                 target.notifyOut(this, e, this.dragData);
20130             }
20131             this.proxy.reset();
20132             if(this.afterDragOut){
20133                 /**
20134                  * An empty function by default, but provided so that you can perform a custom action
20135                  * after the dragged item is dragged out of the target without dropping.
20136                  * @param {Roo.dd.DragDrop} target The drop target
20137                  * @param {Event} e The event object
20138                  * @param {String} id The id of the dragged element
20139                  * @method afterDragOut
20140                  */
20141                 this.afterDragOut(target, e, id);
20142             }
20143         }
20144         this.cachedTarget = null;
20145     },
20146
20147     /**
20148      * An empty function by default, but provided so that you can perform a custom action before the dragged
20149      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20150      * @param {Roo.dd.DragDrop} target The drop target
20151      * @param {Event} e The event object
20152      * @param {String} id The id of the dragged element
20153      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20154      */
20155     beforeDragOut : function(target, e, id){
20156         return true;
20157     },
20158     
20159     // private
20160     onDragDrop : function(e, id){
20161         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20162         if(this.beforeDragDrop(target, e, id) !== false){
20163             if(target.isNotifyTarget){
20164                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20165                     this.onValidDrop(target, e, id);
20166                 }else{
20167                     this.onInvalidDrop(target, e, id);
20168                 }
20169             }else{
20170                 this.onValidDrop(target, e, id);
20171             }
20172             
20173             if(this.afterDragDrop){
20174                 /**
20175                  * An empty function by default, but provided so that you can perform a custom action
20176                  * after a valid drag drop has occurred by providing an implementation.
20177                  * @param {Roo.dd.DragDrop} target The drop target
20178                  * @param {Event} e The event object
20179                  * @param {String} id The id of the dropped element
20180                  * @method afterDragDrop
20181                  */
20182                 this.afterDragDrop(target, e, id);
20183             }
20184         }
20185         delete this.cachedTarget;
20186     },
20187
20188     /**
20189      * An empty function by default, but provided so that you can perform a custom action before the dragged
20190      * item is dropped onto the target and optionally cancel the onDragDrop.
20191      * @param {Roo.dd.DragDrop} target The drop target
20192      * @param {Event} e The event object
20193      * @param {String} id The id of the dragged element
20194      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20195      */
20196     beforeDragDrop : function(target, e, id){
20197         return true;
20198     },
20199
20200     // private
20201     onValidDrop : function(target, e, id){
20202         this.hideProxy();
20203         if(this.afterValidDrop){
20204             /**
20205              * An empty function by default, but provided so that you can perform a custom action
20206              * after a valid drop has occurred by providing an implementation.
20207              * @param {Object} target The target DD 
20208              * @param {Event} e The event object
20209              * @param {String} id The id of the dropped element
20210              * @method afterInvalidDrop
20211              */
20212             this.afterValidDrop(target, e, id);
20213         }
20214     },
20215
20216     // private
20217     getRepairXY : function(e, data){
20218         return this.el.getXY();  
20219     },
20220
20221     // private
20222     onInvalidDrop : function(target, e, id){
20223         this.beforeInvalidDrop(target, e, id);
20224         if(this.cachedTarget){
20225             if(this.cachedTarget.isNotifyTarget){
20226                 this.cachedTarget.notifyOut(this, e, this.dragData);
20227             }
20228             this.cacheTarget = null;
20229         }
20230         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20231
20232         if(this.afterInvalidDrop){
20233             /**
20234              * An empty function by default, but provided so that you can perform a custom action
20235              * after an invalid drop has occurred by providing an implementation.
20236              * @param {Event} e The event object
20237              * @param {String} id The id of the dropped element
20238              * @method afterInvalidDrop
20239              */
20240             this.afterInvalidDrop(e, id);
20241         }
20242     },
20243
20244     // private
20245     afterRepair : function(){
20246         if(Roo.enableFx){
20247             this.el.highlight(this.hlColor || "c3daf9");
20248         }
20249         this.dragging = false;
20250     },
20251
20252     /**
20253      * An empty function by default, but provided so that you can perform a custom action after an invalid
20254      * drop has occurred.
20255      * @param {Roo.dd.DragDrop} target The drop target
20256      * @param {Event} e The event object
20257      * @param {String} id The id of the dragged element
20258      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20259      */
20260     beforeInvalidDrop : function(target, e, id){
20261         return true;
20262     },
20263
20264     // private
20265     handleMouseDown : function(e){
20266         if(this.dragging) {
20267             return;
20268         }
20269         var data = this.getDragData(e);
20270         if(data && this.onBeforeDrag(data, e) !== false){
20271             this.dragData = data;
20272             this.proxy.stop();
20273             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20274         } 
20275     },
20276
20277     /**
20278      * An empty function by default, but provided so that you can perform a custom action before the initial
20279      * drag event begins and optionally cancel it.
20280      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20281      * @param {Event} e The event object
20282      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20283      */
20284     onBeforeDrag : function(data, e){
20285         return true;
20286     },
20287
20288     /**
20289      * An empty function by default, but provided so that you can perform a custom action once the initial
20290      * drag event has begun.  The drag cannot be canceled from this function.
20291      * @param {Number} x The x position of the click on the dragged object
20292      * @param {Number} y The y position of the click on the dragged object
20293      */
20294     onStartDrag : Roo.emptyFn,
20295
20296     // private - YUI override
20297     startDrag : function(x, y){
20298         this.proxy.reset();
20299         this.dragging = true;
20300         this.proxy.update("");
20301         this.onInitDrag(x, y);
20302         this.proxy.show();
20303     },
20304
20305     // private
20306     onInitDrag : function(x, y){
20307         var clone = this.el.dom.cloneNode(true);
20308         clone.id = Roo.id(); // prevent duplicate ids
20309         this.proxy.update(clone);
20310         this.onStartDrag(x, y);
20311         return true;
20312     },
20313
20314     /**
20315      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20316      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20317      */
20318     getProxy : function(){
20319         return this.proxy;  
20320     },
20321
20322     /**
20323      * Hides the drag source's {@link Roo.dd.StatusProxy}
20324      */
20325     hideProxy : function(){
20326         this.proxy.hide();  
20327         this.proxy.reset(true);
20328         this.dragging = false;
20329     },
20330
20331     // private
20332     triggerCacheRefresh : function(){
20333         Roo.dd.DDM.refreshCache(this.groups);
20334     },
20335
20336     // private - override to prevent hiding
20337     b4EndDrag: function(e) {
20338     },
20339
20340     // private - override to prevent moving
20341     endDrag : function(e){
20342         this.onEndDrag(this.dragData, e);
20343     },
20344
20345     // private
20346     onEndDrag : function(data, e){
20347     },
20348     
20349     // private - pin to cursor
20350     autoOffset : function(x, y) {
20351         this.setDelta(-12, -20);
20352     }    
20353 });/*
20354  * Based on:
20355  * Ext JS Library 1.1.1
20356  * Copyright(c) 2006-2007, Ext JS, LLC.
20357  *
20358  * Originally Released Under LGPL - original licence link has changed is not relivant.
20359  *
20360  * Fork - LGPL
20361  * <script type="text/javascript">
20362  */
20363
20364
20365 /**
20366  * @class Roo.dd.DropTarget
20367  * @extends Roo.dd.DDTarget
20368  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20369  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20370  * @constructor
20371  * @param {String/HTMLElement/Element} el The container element
20372  * @param {Object} config
20373  */
20374 Roo.dd.DropTarget = function(el, config){
20375     this.el = Roo.get(el);
20376     
20377     var listeners = false; ;
20378     if (config && config.listeners) {
20379         listeners= config.listeners;
20380         delete config.listeners;
20381     }
20382     Roo.apply(this, config);
20383     
20384     if(this.containerScroll){
20385         Roo.dd.ScrollManager.register(this.el);
20386     }
20387     this.addEvents( {
20388          /**
20389          * @scope Roo.dd.DropTarget
20390          */
20391          
20392          /**
20393          * @event enter
20394          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20395          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20396          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20397          * 
20398          * IMPORTANT : it should set this.overClass and this.dropAllowed
20399          * 
20400          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20401          * @param {Event} e The event
20402          * @param {Object} data An object containing arbitrary data supplied by the drag source
20403          */
20404         "enter" : true,
20405         
20406          /**
20407          * @event over
20408          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20409          * This method will be called on every mouse movement while the drag source is over the drop target.
20410          * This default implementation simply returns the dropAllowed config value.
20411          * 
20412          * IMPORTANT : it should set this.dropAllowed
20413          * 
20414          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20415          * @param {Event} e The event
20416          * @param {Object} data An object containing arbitrary data supplied by the drag source
20417          
20418          */
20419         "over" : true,
20420         /**
20421          * @event out
20422          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20423          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20424          * overClass (if any) from the drop element.
20425          * 
20426          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20427          * @param {Event} e The event
20428          * @param {Object} data An object containing arbitrary data supplied by the drag source
20429          */
20430          "out" : true,
20431          
20432         /**
20433          * @event drop
20434          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20435          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20436          * implementation that does something to process the drop event and returns true so that the drag source's
20437          * repair action does not run.
20438          * 
20439          * IMPORTANT : it should set this.success
20440          * 
20441          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20442          * @param {Event} e The event
20443          * @param {Object} data An object containing arbitrary data supplied by the drag source
20444         */
20445          "drop" : true
20446     });
20447             
20448      
20449     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20450         this.el.dom, 
20451         this.ddGroup || this.group,
20452         {
20453             isTarget: true,
20454             listeners : listeners || {} 
20455            
20456         
20457         }
20458     );
20459
20460 };
20461
20462 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20463     /**
20464      * @cfg {String} overClass
20465      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20466      */
20467      /**
20468      * @cfg {String} ddGroup
20469      * The drag drop group to handle drop events for
20470      */
20471      
20472     /**
20473      * @cfg {String} dropAllowed
20474      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20475      */
20476     dropAllowed : "x-dd-drop-ok",
20477     /**
20478      * @cfg {String} dropNotAllowed
20479      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20480      */
20481     dropNotAllowed : "x-dd-drop-nodrop",
20482     /**
20483      * @cfg {boolean} success
20484      * set this after drop listener.. 
20485      */
20486     success : false,
20487     /**
20488      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20489      * if the drop point is valid for over/enter..
20490      */
20491     valid : false,
20492     // private
20493     isTarget : true,
20494
20495     // private
20496     isNotifyTarget : true,
20497     
20498     /**
20499      * @hide
20500      */
20501     notifyEnter : function(dd, e, data)
20502     {
20503         this.valid = true;
20504         this.fireEvent('enter', dd, e, data);
20505         if(this.overClass){
20506             this.el.addClass(this.overClass);
20507         }
20508         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20509             this.valid ? this.dropAllowed : this.dropNotAllowed
20510         );
20511     },
20512
20513     /**
20514      * @hide
20515      */
20516     notifyOver : function(dd, e, data)
20517     {
20518         this.valid = true;
20519         this.fireEvent('over', dd, e, data);
20520         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20521             this.valid ? this.dropAllowed : this.dropNotAllowed
20522         );
20523     },
20524
20525     /**
20526      * @hide
20527      */
20528     notifyOut : function(dd, e, data)
20529     {
20530         this.fireEvent('out', dd, e, data);
20531         if(this.overClass){
20532             this.el.removeClass(this.overClass);
20533         }
20534     },
20535
20536     /**
20537      * @hide
20538      */
20539     notifyDrop : function(dd, e, data)
20540     {
20541         this.success = false;
20542         this.fireEvent('drop', dd, e, data);
20543         return this.success;
20544     }
20545 });/*
20546  * Based on:
20547  * Ext JS Library 1.1.1
20548  * Copyright(c) 2006-2007, Ext JS, LLC.
20549  *
20550  * Originally Released Under LGPL - original licence link has changed is not relivant.
20551  *
20552  * Fork - LGPL
20553  * <script type="text/javascript">
20554  */
20555
20556
20557 /**
20558  * @class Roo.dd.DragZone
20559  * @extends Roo.dd.DragSource
20560  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20561  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20562  * @constructor
20563  * @param {String/HTMLElement/Element} el The container element
20564  * @param {Object} config
20565  */
20566 Roo.dd.DragZone = function(el, config){
20567     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20568     if(this.containerScroll){
20569         Roo.dd.ScrollManager.register(this.el);
20570     }
20571 };
20572
20573 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20574     /**
20575      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20576      * for auto scrolling during drag operations.
20577      */
20578     /**
20579      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20580      * method after a failed drop (defaults to "c3daf9" - light blue)
20581      */
20582
20583     /**
20584      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20585      * for a valid target to drag based on the mouse down. Override this method
20586      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20587      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20588      * @param {EventObject} e The mouse down event
20589      * @return {Object} The dragData
20590      */
20591     getDragData : function(e){
20592         return Roo.dd.Registry.getHandleFromEvent(e);
20593     },
20594     
20595     /**
20596      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20597      * this.dragData.ddel
20598      * @param {Number} x The x position of the click on the dragged object
20599      * @param {Number} y The y position of the click on the dragged object
20600      * @return {Boolean} true to continue the drag, false to cancel
20601      */
20602     onInitDrag : function(x, y){
20603         this.proxy.update(this.dragData.ddel.cloneNode(true));
20604         this.onStartDrag(x, y);
20605         return true;
20606     },
20607     
20608     /**
20609      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20610      */
20611     afterRepair : function(){
20612         if(Roo.enableFx){
20613             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20614         }
20615         this.dragging = false;
20616     },
20617
20618     /**
20619      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20620      * the XY of this.dragData.ddel
20621      * @param {EventObject} e The mouse up event
20622      * @return {Array} The xy location (e.g. [100, 200])
20623      */
20624     getRepairXY : function(e){
20625         return Roo.Element.fly(this.dragData.ddel).getXY();  
20626     }
20627 });/*
20628  * Based on:
20629  * Ext JS Library 1.1.1
20630  * Copyright(c) 2006-2007, Ext JS, LLC.
20631  *
20632  * Originally Released Under LGPL - original licence link has changed is not relivant.
20633  *
20634  * Fork - LGPL
20635  * <script type="text/javascript">
20636  */
20637 /**
20638  * @class Roo.dd.DropZone
20639  * @extends Roo.dd.DropTarget
20640  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20641  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20642  * @constructor
20643  * @param {String/HTMLElement/Element} el The container element
20644  * @param {Object} config
20645  */
20646 Roo.dd.DropZone = function(el, config){
20647     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20648 };
20649
20650 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20651     /**
20652      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20653      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20654      * provide your own custom lookup.
20655      * @param {Event} e The event
20656      * @return {Object} data The custom data
20657      */
20658     getTargetFromEvent : function(e){
20659         return Roo.dd.Registry.getTargetFromEvent(e);
20660     },
20661
20662     /**
20663      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20664      * that it has registered.  This method has no default implementation and should be overridden to provide
20665      * node-specific processing if necessary.
20666      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20667      * {@link #getTargetFromEvent} for this node)
20668      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20669      * @param {Event} e The event
20670      * @param {Object} data An object containing arbitrary data supplied by the drag source
20671      */
20672     onNodeEnter : function(n, dd, e, data){
20673         
20674     },
20675
20676     /**
20677      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20678      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20679      * overridden to provide the proper feedback.
20680      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20681      * {@link #getTargetFromEvent} for this node)
20682      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20683      * @param {Event} e The event
20684      * @param {Object} data An object containing arbitrary data supplied by the drag source
20685      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20686      * underlying {@link Roo.dd.StatusProxy} can be updated
20687      */
20688     onNodeOver : function(n, dd, e, data){
20689         return this.dropAllowed;
20690     },
20691
20692     /**
20693      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20694      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20695      * node-specific processing if necessary.
20696      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20697      * {@link #getTargetFromEvent} for this node)
20698      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20699      * @param {Event} e The event
20700      * @param {Object} data An object containing arbitrary data supplied by the drag source
20701      */
20702     onNodeOut : function(n, dd, e, data){
20703         
20704     },
20705
20706     /**
20707      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20708      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20709      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20710      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20711      * {@link #getTargetFromEvent} for this node)
20712      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20713      * @param {Event} e The event
20714      * @param {Object} data An object containing arbitrary data supplied by the drag source
20715      * @return {Boolean} True if the drop was valid, else false
20716      */
20717     onNodeDrop : function(n, dd, e, data){
20718         return false;
20719     },
20720
20721     /**
20722      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20723      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20724      * it should be overridden to provide the proper feedback if necessary.
20725      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20726      * @param {Event} e The event
20727      * @param {Object} data An object containing arbitrary data supplied by the drag source
20728      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20729      * underlying {@link Roo.dd.StatusProxy} can be updated
20730      */
20731     onContainerOver : function(dd, e, data){
20732         return this.dropNotAllowed;
20733     },
20734
20735     /**
20736      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20737      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20738      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20739      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20740      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20741      * @param {Event} e The event
20742      * @param {Object} data An object containing arbitrary data supplied by the drag source
20743      * @return {Boolean} True if the drop was valid, else false
20744      */
20745     onContainerDrop : function(dd, e, data){
20746         return false;
20747     },
20748
20749     /**
20750      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20751      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20752      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20753      * you should override this method and provide a custom implementation.
20754      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20755      * @param {Event} e The event
20756      * @param {Object} data An object containing arbitrary data supplied by the drag source
20757      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20758      * underlying {@link Roo.dd.StatusProxy} can be updated
20759      */
20760     notifyEnter : function(dd, e, data){
20761         return this.dropNotAllowed;
20762     },
20763
20764     /**
20765      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20766      * This method will be called on every mouse movement while the drag source is over the drop zone.
20767      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20768      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20769      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20770      * registered node, it will call {@link #onContainerOver}.
20771      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20772      * @param {Event} e The event
20773      * @param {Object} data An object containing arbitrary data supplied by the drag source
20774      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20775      * underlying {@link Roo.dd.StatusProxy} can be updated
20776      */
20777     notifyOver : function(dd, e, data){
20778         var n = this.getTargetFromEvent(e);
20779         if(!n){ // not over valid drop target
20780             if(this.lastOverNode){
20781                 this.onNodeOut(this.lastOverNode, dd, e, data);
20782                 this.lastOverNode = null;
20783             }
20784             return this.onContainerOver(dd, e, data);
20785         }
20786         if(this.lastOverNode != n){
20787             if(this.lastOverNode){
20788                 this.onNodeOut(this.lastOverNode, dd, e, data);
20789             }
20790             this.onNodeEnter(n, dd, e, data);
20791             this.lastOverNode = n;
20792         }
20793         return this.onNodeOver(n, dd, e, data);
20794     },
20795
20796     /**
20797      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20798      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20799      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20800      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20801      * @param {Event} e The event
20802      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20803      */
20804     notifyOut : function(dd, e, data){
20805         if(this.lastOverNode){
20806             this.onNodeOut(this.lastOverNode, dd, e, data);
20807             this.lastOverNode = null;
20808         }
20809     },
20810
20811     /**
20812      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20813      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20814      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20815      * otherwise it will call {@link #onContainerDrop}.
20816      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20817      * @param {Event} e The event
20818      * @param {Object} data An object containing arbitrary data supplied by the drag source
20819      * @return {Boolean} True if the drop was valid, else false
20820      */
20821     notifyDrop : function(dd, e, data){
20822         if(this.lastOverNode){
20823             this.onNodeOut(this.lastOverNode, dd, e, data);
20824             this.lastOverNode = null;
20825         }
20826         var n = this.getTargetFromEvent(e);
20827         return n ?
20828             this.onNodeDrop(n, dd, e, data) :
20829             this.onContainerDrop(dd, e, data);
20830     },
20831
20832     // private
20833     triggerCacheRefresh : function(){
20834         Roo.dd.DDM.refreshCache(this.groups);
20835     }  
20836 });/*
20837  * Based on:
20838  * Ext JS Library 1.1.1
20839  * Copyright(c) 2006-2007, Ext JS, LLC.
20840  *
20841  * Originally Released Under LGPL - original licence link has changed is not relivant.
20842  *
20843  * Fork - LGPL
20844  * <script type="text/javascript">
20845  */
20846
20847
20848 /**
20849  * @class Roo.data.SortTypes
20850  * @singleton
20851  * Defines the default sorting (casting?) comparison functions used when sorting data.
20852  */
20853 Roo.data.SortTypes = {
20854     /**
20855      * Default sort that does nothing
20856      * @param {Mixed} s The value being converted
20857      * @return {Mixed} The comparison value
20858      */
20859     none : function(s){
20860         return s;
20861     },
20862     
20863     /**
20864      * The regular expression used to strip tags
20865      * @type {RegExp}
20866      * @property
20867      */
20868     stripTagsRE : /<\/?[^>]+>/gi,
20869     
20870     /**
20871      * Strips all HTML tags to sort on text only
20872      * @param {Mixed} s The value being converted
20873      * @return {String} The comparison value
20874      */
20875     asText : function(s){
20876         return String(s).replace(this.stripTagsRE, "");
20877     },
20878     
20879     /**
20880      * Strips all HTML tags to sort on text only - Case insensitive
20881      * @param {Mixed} s The value being converted
20882      * @return {String} The comparison value
20883      */
20884     asUCText : function(s){
20885         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20886     },
20887     
20888     /**
20889      * Case insensitive string
20890      * @param {Mixed} s The value being converted
20891      * @return {String} The comparison value
20892      */
20893     asUCString : function(s) {
20894         return String(s).toUpperCase();
20895     },
20896     
20897     /**
20898      * Date sorting
20899      * @param {Mixed} s The value being converted
20900      * @return {Number} The comparison value
20901      */
20902     asDate : function(s) {
20903         if(!s){
20904             return 0;
20905         }
20906         if(s instanceof Date){
20907             return s.getTime();
20908         }
20909         return Date.parse(String(s));
20910     },
20911     
20912     /**
20913      * Float sorting
20914      * @param {Mixed} s The value being converted
20915      * @return {Float} The comparison value
20916      */
20917     asFloat : function(s) {
20918         var val = parseFloat(String(s).replace(/,/g, ""));
20919         if(isNaN(val)) val = 0;
20920         return val;
20921     },
20922     
20923     /**
20924      * Integer sorting
20925      * @param {Mixed} s The value being converted
20926      * @return {Number} The comparison value
20927      */
20928     asInt : function(s) {
20929         var val = parseInt(String(s).replace(/,/g, ""));
20930         if(isNaN(val)) val = 0;
20931         return val;
20932     }
20933 };/*
20934  * Based on:
20935  * Ext JS Library 1.1.1
20936  * Copyright(c) 2006-2007, Ext JS, LLC.
20937  *
20938  * Originally Released Under LGPL - original licence link has changed is not relivant.
20939  *
20940  * Fork - LGPL
20941  * <script type="text/javascript">
20942  */
20943
20944 /**
20945 * @class Roo.data.Record
20946  * Instances of this class encapsulate both record <em>definition</em> information, and record
20947  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20948  * to access Records cached in an {@link Roo.data.Store} object.<br>
20949  * <p>
20950  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20951  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20952  * objects.<br>
20953  * <p>
20954  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20955  * @constructor
20956  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20957  * {@link #create}. The parameters are the same.
20958  * @param {Array} data An associative Array of data values keyed by the field name.
20959  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20960  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20961  * not specified an integer id is generated.
20962  */
20963 Roo.data.Record = function(data, id){
20964     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20965     this.data = data;
20966 };
20967
20968 /**
20969  * Generate a constructor for a specific record layout.
20970  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20971  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20972  * Each field definition object may contain the following properties: <ul>
20973  * <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,
20974  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20975  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20976  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20977  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20978  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20979  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20980  * this may be omitted.</p></li>
20981  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20982  * <ul><li>auto (Default, implies no conversion)</li>
20983  * <li>string</li>
20984  * <li>int</li>
20985  * <li>float</li>
20986  * <li>boolean</li>
20987  * <li>date</li></ul></p></li>
20988  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20989  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20990  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20991  * by the Reader into an object that will be stored in the Record. It is passed the
20992  * following parameters:<ul>
20993  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20994  * </ul></p></li>
20995  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20996  * </ul>
20997  * <br>usage:<br><pre><code>
20998 var TopicRecord = Roo.data.Record.create(
20999     {name: 'title', mapping: 'topic_title'},
21000     {name: 'author', mapping: 'username'},
21001     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
21002     {name: 'lastPost', mapping: 'post_time', type: 'date'},
21003     {name: 'lastPoster', mapping: 'user2'},
21004     {name: 'excerpt', mapping: 'post_text'}
21005 );
21006
21007 var myNewRecord = new TopicRecord({
21008     title: 'Do my job please',
21009     author: 'noobie',
21010     totalPosts: 1,
21011     lastPost: new Date(),
21012     lastPoster: 'Animal',
21013     excerpt: 'No way dude!'
21014 });
21015 myStore.add(myNewRecord);
21016 </code></pre>
21017  * @method create
21018  * @static
21019  */
21020 Roo.data.Record.create = function(o){
21021     var f = function(){
21022         f.superclass.constructor.apply(this, arguments);
21023     };
21024     Roo.extend(f, Roo.data.Record);
21025     var p = f.prototype;
21026     p.fields = new Roo.util.MixedCollection(false, function(field){
21027         return field.name;
21028     });
21029     for(var i = 0, len = o.length; i < len; i++){
21030         p.fields.add(new Roo.data.Field(o[i]));
21031     }
21032     f.getField = function(name){
21033         return p.fields.get(name);  
21034     };
21035     return f;
21036 };
21037
21038 Roo.data.Record.AUTO_ID = 1000;
21039 Roo.data.Record.EDIT = 'edit';
21040 Roo.data.Record.REJECT = 'reject';
21041 Roo.data.Record.COMMIT = 'commit';
21042
21043 Roo.data.Record.prototype = {
21044     /**
21045      * Readonly flag - true if this record has been modified.
21046      * @type Boolean
21047      */
21048     dirty : false,
21049     editing : false,
21050     error: null,
21051     modified: null,
21052
21053     // private
21054     join : function(store){
21055         this.store = store;
21056     },
21057
21058     /**
21059      * Set the named field to the specified value.
21060      * @param {String} name The name of the field to set.
21061      * @param {Object} value The value to set the field to.
21062      */
21063     set : function(name, value){
21064         if(this.data[name] == value){
21065             return;
21066         }
21067         this.dirty = true;
21068         if(!this.modified){
21069             this.modified = {};
21070         }
21071         if(typeof this.modified[name] == 'undefined'){
21072             this.modified[name] = this.data[name];
21073         }
21074         this.data[name] = value;
21075         if(!this.editing && this.store){
21076             this.store.afterEdit(this);
21077         }       
21078     },
21079
21080     /**
21081      * Get the value of the named field.
21082      * @param {String} name The name of the field to get the value of.
21083      * @return {Object} The value of the field.
21084      */
21085     get : function(name){
21086         return this.data[name]; 
21087     },
21088
21089     // private
21090     beginEdit : function(){
21091         this.editing = true;
21092         this.modified = {}; 
21093     },
21094
21095     // private
21096     cancelEdit : function(){
21097         this.editing = false;
21098         delete this.modified;
21099     },
21100
21101     // private
21102     endEdit : function(){
21103         this.editing = false;
21104         if(this.dirty && this.store){
21105             this.store.afterEdit(this);
21106         }
21107     },
21108
21109     /**
21110      * Usually called by the {@link Roo.data.Store} which owns the Record.
21111      * Rejects all changes made to the Record since either creation, or the last commit operation.
21112      * Modified fields are reverted to their original values.
21113      * <p>
21114      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21115      * of reject operations.
21116      */
21117     reject : function(){
21118         var m = this.modified;
21119         for(var n in m){
21120             if(typeof m[n] != "function"){
21121                 this.data[n] = m[n];
21122             }
21123         }
21124         this.dirty = false;
21125         delete this.modified;
21126         this.editing = false;
21127         if(this.store){
21128             this.store.afterReject(this);
21129         }
21130     },
21131
21132     /**
21133      * Usually called by the {@link Roo.data.Store} which owns the Record.
21134      * Commits all changes made to the Record since either creation, or the last commit operation.
21135      * <p>
21136      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21137      * of commit operations.
21138      */
21139     commit : function(){
21140         this.dirty = false;
21141         delete this.modified;
21142         this.editing = false;
21143         if(this.store){
21144             this.store.afterCommit(this);
21145         }
21146     },
21147
21148     // private
21149     hasError : function(){
21150         return this.error != null;
21151     },
21152
21153     // private
21154     clearError : function(){
21155         this.error = null;
21156     },
21157
21158     /**
21159      * Creates a copy of this record.
21160      * @param {String} id (optional) A new record id if you don't want to use this record's id
21161      * @return {Record}
21162      */
21163     copy : function(newId) {
21164         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21165     }
21166 };/*
21167  * Based on:
21168  * Ext JS Library 1.1.1
21169  * Copyright(c) 2006-2007, Ext JS, LLC.
21170  *
21171  * Originally Released Under LGPL - original licence link has changed is not relivant.
21172  *
21173  * Fork - LGPL
21174  * <script type="text/javascript">
21175  */
21176
21177
21178
21179 /**
21180  * @class Roo.data.Store
21181  * @extends Roo.util.Observable
21182  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21183  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21184  * <p>
21185  * 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
21186  * has no knowledge of the format of the data returned by the Proxy.<br>
21187  * <p>
21188  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21189  * instances from the data object. These records are cached and made available through accessor functions.
21190  * @constructor
21191  * Creates a new Store.
21192  * @param {Object} config A config object containing the objects needed for the Store to access data,
21193  * and read the data into Records.
21194  */
21195 Roo.data.Store = function(config){
21196     this.data = new Roo.util.MixedCollection(false);
21197     this.data.getKey = function(o){
21198         return o.id;
21199     };
21200     this.baseParams = {};
21201     // private
21202     this.paramNames = {
21203         "start" : "start",
21204         "limit" : "limit",
21205         "sort" : "sort",
21206         "dir" : "dir",
21207         "multisort" : "_multisort"
21208     };
21209
21210     if(config && config.data){
21211         this.inlineData = config.data;
21212         delete config.data;
21213     }
21214
21215     Roo.apply(this, config);
21216     
21217     if(this.reader){ // reader passed
21218         this.reader = Roo.factory(this.reader, Roo.data);
21219         this.reader.xmodule = this.xmodule || false;
21220         if(!this.recordType){
21221             this.recordType = this.reader.recordType;
21222         }
21223         if(this.reader.onMetaChange){
21224             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21225         }
21226     }
21227
21228     if(this.recordType){
21229         this.fields = this.recordType.prototype.fields;
21230     }
21231     this.modified = [];
21232
21233     this.addEvents({
21234         /**
21235          * @event datachanged
21236          * Fires when the data cache has changed, and a widget which is using this Store
21237          * as a Record cache should refresh its view.
21238          * @param {Store} this
21239          */
21240         datachanged : true,
21241         /**
21242          * @event metachange
21243          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21244          * @param {Store} this
21245          * @param {Object} meta The JSON metadata
21246          */
21247         metachange : true,
21248         /**
21249          * @event add
21250          * Fires when Records have been added to the Store
21251          * @param {Store} this
21252          * @param {Roo.data.Record[]} records The array of Records added
21253          * @param {Number} index The index at which the record(s) were added
21254          */
21255         add : true,
21256         /**
21257          * @event remove
21258          * Fires when a Record has been removed from the Store
21259          * @param {Store} this
21260          * @param {Roo.data.Record} record The Record that was removed
21261          * @param {Number} index The index at which the record was removed
21262          */
21263         remove : true,
21264         /**
21265          * @event update
21266          * Fires when a Record has been updated
21267          * @param {Store} this
21268          * @param {Roo.data.Record} record The Record that was updated
21269          * @param {String} operation The update operation being performed.  Value may be one of:
21270          * <pre><code>
21271  Roo.data.Record.EDIT
21272  Roo.data.Record.REJECT
21273  Roo.data.Record.COMMIT
21274          * </code></pre>
21275          */
21276         update : true,
21277         /**
21278          * @event clear
21279          * Fires when the data cache has been cleared.
21280          * @param {Store} this
21281          */
21282         clear : true,
21283         /**
21284          * @event beforeload
21285          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21286          * the load action will be canceled.
21287          * @param {Store} this
21288          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21289          */
21290         beforeload : true,
21291         /**
21292          * @event beforeloadadd
21293          * Fires after a new set of Records has been loaded.
21294          * @param {Store} this
21295          * @param {Roo.data.Record[]} records The Records that were loaded
21296          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21297          */
21298         beforeloadadd : true,
21299         /**
21300          * @event load
21301          * Fires after a new set of Records has been loaded, before they are added to the store.
21302          * @param {Store} this
21303          * @param {Roo.data.Record[]} records The Records that were loaded
21304          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21305          * @params {Object} return from reader
21306          */
21307         load : true,
21308         /**
21309          * @event loadexception
21310          * Fires if an exception occurs in the Proxy during loading.
21311          * Called with the signature of the Proxy's "loadexception" event.
21312          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21313          * 
21314          * @param {Proxy} 
21315          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21316          * @param {Object} load options 
21317          * @param {Object} jsonData from your request (normally this contains the Exception)
21318          */
21319         loadexception : true
21320     });
21321     
21322     if(this.proxy){
21323         this.proxy = Roo.factory(this.proxy, Roo.data);
21324         this.proxy.xmodule = this.xmodule || false;
21325         this.relayEvents(this.proxy,  ["loadexception"]);
21326     }
21327     this.sortToggle = {};
21328     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21329
21330     Roo.data.Store.superclass.constructor.call(this);
21331
21332     if(this.inlineData){
21333         this.loadData(this.inlineData);
21334         delete this.inlineData;
21335     }
21336 };
21337
21338 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21339      /**
21340     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21341     * without a remote query - used by combo/forms at present.
21342     */
21343     
21344     /**
21345     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21346     */
21347     /**
21348     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21349     */
21350     /**
21351     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21352     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21353     */
21354     /**
21355     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21356     * on any HTTP request
21357     */
21358     /**
21359     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21360     */
21361     /**
21362     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21363     */
21364     multiSort: false,
21365     /**
21366     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21367     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21368     */
21369     remoteSort : false,
21370
21371     /**
21372     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21373      * loaded or when a record is removed. (defaults to false).
21374     */
21375     pruneModifiedRecords : false,
21376
21377     // private
21378     lastOptions : null,
21379
21380     /**
21381      * Add Records to the Store and fires the add event.
21382      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21383      */
21384     add : function(records){
21385         records = [].concat(records);
21386         for(var i = 0, len = records.length; i < len; i++){
21387             records[i].join(this);
21388         }
21389         var index = this.data.length;
21390         this.data.addAll(records);
21391         this.fireEvent("add", this, records, index);
21392     },
21393
21394     /**
21395      * Remove a Record from the Store and fires the remove event.
21396      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21397      */
21398     remove : function(record){
21399         var index = this.data.indexOf(record);
21400         this.data.removeAt(index);
21401         if(this.pruneModifiedRecords){
21402             this.modified.remove(record);
21403         }
21404         this.fireEvent("remove", this, record, index);
21405     },
21406
21407     /**
21408      * Remove all Records from the Store and fires the clear event.
21409      */
21410     removeAll : function(){
21411         this.data.clear();
21412         if(this.pruneModifiedRecords){
21413             this.modified = [];
21414         }
21415         this.fireEvent("clear", this);
21416     },
21417
21418     /**
21419      * Inserts Records to the Store at the given index and fires the add event.
21420      * @param {Number} index The start index at which to insert the passed Records.
21421      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21422      */
21423     insert : function(index, records){
21424         records = [].concat(records);
21425         for(var i = 0, len = records.length; i < len; i++){
21426             this.data.insert(index, records[i]);
21427             records[i].join(this);
21428         }
21429         this.fireEvent("add", this, records, index);
21430     },
21431
21432     /**
21433      * Get the index within the cache of the passed Record.
21434      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21435      * @return {Number} The index of the passed Record. Returns -1 if not found.
21436      */
21437     indexOf : function(record){
21438         return this.data.indexOf(record);
21439     },
21440
21441     /**
21442      * Get the index within the cache of the Record with the passed id.
21443      * @param {String} id The id of the Record to find.
21444      * @return {Number} The index of the Record. Returns -1 if not found.
21445      */
21446     indexOfId : function(id){
21447         return this.data.indexOfKey(id);
21448     },
21449
21450     /**
21451      * Get the Record with the specified id.
21452      * @param {String} id The id of the Record to find.
21453      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21454      */
21455     getById : function(id){
21456         return this.data.key(id);
21457     },
21458
21459     /**
21460      * Get the Record at the specified index.
21461      * @param {Number} index The index of the Record to find.
21462      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21463      */
21464     getAt : function(index){
21465         return this.data.itemAt(index);
21466     },
21467
21468     /**
21469      * Returns a range of Records between specified indices.
21470      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21471      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21472      * @return {Roo.data.Record[]} An array of Records
21473      */
21474     getRange : function(start, end){
21475         return this.data.getRange(start, end);
21476     },
21477
21478     // private
21479     storeOptions : function(o){
21480         o = Roo.apply({}, o);
21481         delete o.callback;
21482         delete o.scope;
21483         this.lastOptions = o;
21484     },
21485
21486     /**
21487      * Loads the Record cache from the configured Proxy using the configured Reader.
21488      * <p>
21489      * If using remote paging, then the first load call must specify the <em>start</em>
21490      * and <em>limit</em> properties in the options.params property to establish the initial
21491      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21492      * <p>
21493      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21494      * and this call will return before the new data has been loaded. Perform any post-processing
21495      * in a callback function, or in a "load" event handler.</strong>
21496      * <p>
21497      * @param {Object} options An object containing properties which control loading options:<ul>
21498      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21499      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21500      * passed the following arguments:<ul>
21501      * <li>r : Roo.data.Record[]</li>
21502      * <li>options: Options object from the load call</li>
21503      * <li>success: Boolean success indicator</li></ul></li>
21504      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21505      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21506      * </ul>
21507      */
21508     load : function(options){
21509         options = options || {};
21510         if(this.fireEvent("beforeload", this, options) !== false){
21511             this.storeOptions(options);
21512             var p = Roo.apply(options.params || {}, this.baseParams);
21513             // if meta was not loaded from remote source.. try requesting it.
21514             if (!this.reader.metaFromRemote) {
21515                 p._requestMeta = 1;
21516             }
21517             if(this.sortInfo && this.remoteSort){
21518                 var pn = this.paramNames;
21519                 p[pn["sort"]] = this.sortInfo.field;
21520                 p[pn["dir"]] = this.sortInfo.direction;
21521             }
21522             if (this.multiSort) {
21523                 var pn = this.paramNames;
21524                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21525             }
21526             
21527             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21528         }
21529     },
21530
21531     /**
21532      * Reloads the Record cache from the configured Proxy using the configured Reader and
21533      * the options from the last load operation performed.
21534      * @param {Object} options (optional) An object containing properties which may override the options
21535      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21536      * the most recently used options are reused).
21537      */
21538     reload : function(options){
21539         this.load(Roo.applyIf(options||{}, this.lastOptions));
21540     },
21541
21542     // private
21543     // Called as a callback by the Reader during a load operation.
21544     loadRecords : function(o, options, success){
21545         if(!o || success === false){
21546             if(success !== false){
21547                 this.fireEvent("load", this, [], options, o);
21548             }
21549             if(options.callback){
21550                 options.callback.call(options.scope || this, [], options, false);
21551             }
21552             return;
21553         }
21554         // if data returned failure - throw an exception.
21555         if (o.success === false) {
21556             // show a message if no listener is registered.
21557             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21558                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21559             }
21560             // loadmask wil be hooked into this..
21561             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21562             return;
21563         }
21564         var r = o.records, t = o.totalRecords || r.length;
21565         
21566         this.fireEvent("beforeloadadd", this, r, options, o);
21567         
21568         if(!options || options.add !== true){
21569             if(this.pruneModifiedRecords){
21570                 this.modified = [];
21571             }
21572             for(var i = 0, len = r.length; i < len; i++){
21573                 r[i].join(this);
21574             }
21575             if(this.snapshot){
21576                 this.data = this.snapshot;
21577                 delete this.snapshot;
21578             }
21579             this.data.clear();
21580             this.data.addAll(r);
21581             this.totalLength = t;
21582             this.applySort();
21583             this.fireEvent("datachanged", this);
21584         }else{
21585             this.totalLength = Math.max(t, this.data.length+r.length);
21586             this.add(r);
21587         }
21588         this.fireEvent("load", this, r, options, o);
21589         if(options.callback){
21590             options.callback.call(options.scope || this, r, options, true);
21591         }
21592     },
21593
21594
21595     /**
21596      * Loads data from a passed data block. A Reader which understands the format of the data
21597      * must have been configured in the constructor.
21598      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21599      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21600      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21601      */
21602     loadData : function(o, append){
21603         var r = this.reader.readRecords(o);
21604         this.loadRecords(r, {add: append}, true);
21605     },
21606
21607     /**
21608      * Gets the number of cached records.
21609      * <p>
21610      * <em>If using paging, this may not be the total size of the dataset. If the data object
21611      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21612      * the data set size</em>
21613      */
21614     getCount : function(){
21615         return this.data.length || 0;
21616     },
21617
21618     /**
21619      * Gets the total number of records in the dataset as returned by the server.
21620      * <p>
21621      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21622      * the dataset size</em>
21623      */
21624     getTotalCount : function(){
21625         return this.totalLength || 0;
21626     },
21627
21628     /**
21629      * Returns the sort state of the Store as an object with two properties:
21630      * <pre><code>
21631  field {String} The name of the field by which the Records are sorted
21632  direction {String} The sort order, "ASC" or "DESC"
21633      * </code></pre>
21634      */
21635     getSortState : function(){
21636         return this.sortInfo;
21637     },
21638
21639     // private
21640     applySort : function(){
21641         if(this.sortInfo && !this.remoteSort){
21642             var s = this.sortInfo, f = s.field;
21643             var st = this.fields.get(f).sortType;
21644             var fn = function(r1, r2){
21645                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21646                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21647             };
21648             this.data.sort(s.direction, fn);
21649             if(this.snapshot && this.snapshot != this.data){
21650                 this.snapshot.sort(s.direction, fn);
21651             }
21652         }
21653     },
21654
21655     /**
21656      * Sets the default sort column and order to be used by the next load operation.
21657      * @param {String} fieldName The name of the field to sort by.
21658      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21659      */
21660     setDefaultSort : function(field, dir){
21661         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21662     },
21663
21664     /**
21665      * Sort the Records.
21666      * If remote sorting is used, the sort is performed on the server, and the cache is
21667      * reloaded. If local sorting is used, the cache is sorted internally.
21668      * @param {String} fieldName The name of the field to sort by.
21669      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21670      */
21671     sort : function(fieldName, dir){
21672         var f = this.fields.get(fieldName);
21673         if(!dir){
21674             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21675             
21676             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21677                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21678             }else{
21679                 dir = f.sortDir;
21680             }
21681         }
21682         this.sortToggle[f.name] = dir;
21683         this.sortInfo = {field: f.name, direction: dir};
21684         if(!this.remoteSort){
21685             this.applySort();
21686             this.fireEvent("datachanged", this);
21687         }else{
21688             this.load(this.lastOptions);
21689         }
21690     },
21691
21692     /**
21693      * Calls the specified function for each of the Records in the cache.
21694      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21695      * Returning <em>false</em> aborts and exits the iteration.
21696      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21697      */
21698     each : function(fn, scope){
21699         this.data.each(fn, scope);
21700     },
21701
21702     /**
21703      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21704      * (e.g., during paging).
21705      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21706      */
21707     getModifiedRecords : function(){
21708         return this.modified;
21709     },
21710
21711     // private
21712     createFilterFn : function(property, value, anyMatch){
21713         if(!value.exec){ // not a regex
21714             value = String(value);
21715             if(value.length == 0){
21716                 return false;
21717             }
21718             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21719         }
21720         return function(r){
21721             return value.test(r.data[property]);
21722         };
21723     },
21724
21725     /**
21726      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21727      * @param {String} property A field on your records
21728      * @param {Number} start The record index to start at (defaults to 0)
21729      * @param {Number} end The last record index to include (defaults to length - 1)
21730      * @return {Number} The sum
21731      */
21732     sum : function(property, start, end){
21733         var rs = this.data.items, v = 0;
21734         start = start || 0;
21735         end = (end || end === 0) ? end : rs.length-1;
21736
21737         for(var i = start; i <= end; i++){
21738             v += (rs[i].data[property] || 0);
21739         }
21740         return v;
21741     },
21742
21743     /**
21744      * Filter the records by a specified property.
21745      * @param {String} field A field on your records
21746      * @param {String/RegExp} value Either a string that the field
21747      * should start with or a RegExp to test against the field
21748      * @param {Boolean} anyMatch True to match any part not just the beginning
21749      */
21750     filter : function(property, value, anyMatch){
21751         var fn = this.createFilterFn(property, value, anyMatch);
21752         return fn ? this.filterBy(fn) : this.clearFilter();
21753     },
21754
21755     /**
21756      * Filter by a function. The specified function will be called with each
21757      * record in this data source. If the function returns true the record is included,
21758      * otherwise it is filtered.
21759      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21760      * @param {Object} scope (optional) The scope of the function (defaults to this)
21761      */
21762     filterBy : function(fn, scope){
21763         this.snapshot = this.snapshot || this.data;
21764         this.data = this.queryBy(fn, scope||this);
21765         this.fireEvent("datachanged", this);
21766     },
21767
21768     /**
21769      * Query the records by a specified property.
21770      * @param {String} field A field on your records
21771      * @param {String/RegExp} value Either a string that the field
21772      * should start with or a RegExp to test against the field
21773      * @param {Boolean} anyMatch True to match any part not just the beginning
21774      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21775      */
21776     query : function(property, value, anyMatch){
21777         var fn = this.createFilterFn(property, value, anyMatch);
21778         return fn ? this.queryBy(fn) : this.data.clone();
21779     },
21780
21781     /**
21782      * Query by a function. The specified function will be called with each
21783      * record in this data source. If the function returns true the record is included
21784      * in the results.
21785      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21786      * @param {Object} scope (optional) The scope of the function (defaults to this)
21787       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21788      **/
21789     queryBy : function(fn, scope){
21790         var data = this.snapshot || this.data;
21791         return data.filterBy(fn, scope||this);
21792     },
21793
21794     /**
21795      * Collects unique values for a particular dataIndex from this store.
21796      * @param {String} dataIndex The property to collect
21797      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21798      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21799      * @return {Array} An array of the unique values
21800      **/
21801     collect : function(dataIndex, allowNull, bypassFilter){
21802         var d = (bypassFilter === true && this.snapshot) ?
21803                 this.snapshot.items : this.data.items;
21804         var v, sv, r = [], l = {};
21805         for(var i = 0, len = d.length; i < len; i++){
21806             v = d[i].data[dataIndex];
21807             sv = String(v);
21808             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21809                 l[sv] = true;
21810                 r[r.length] = v;
21811             }
21812         }
21813         return r;
21814     },
21815
21816     /**
21817      * Revert to a view of the Record cache with no filtering applied.
21818      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21819      */
21820     clearFilter : function(suppressEvent){
21821         if(this.snapshot && this.snapshot != this.data){
21822             this.data = this.snapshot;
21823             delete this.snapshot;
21824             if(suppressEvent !== true){
21825                 this.fireEvent("datachanged", this);
21826             }
21827         }
21828     },
21829
21830     // private
21831     afterEdit : function(record){
21832         if(this.modified.indexOf(record) == -1){
21833             this.modified.push(record);
21834         }
21835         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21836     },
21837     
21838     // private
21839     afterReject : function(record){
21840         this.modified.remove(record);
21841         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21842     },
21843
21844     // private
21845     afterCommit : function(record){
21846         this.modified.remove(record);
21847         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21848     },
21849
21850     /**
21851      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21852      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21853      */
21854     commitChanges : function(){
21855         var m = this.modified.slice(0);
21856         this.modified = [];
21857         for(var i = 0, len = m.length; i < len; i++){
21858             m[i].commit();
21859         }
21860     },
21861
21862     /**
21863      * Cancel outstanding changes on all changed records.
21864      */
21865     rejectChanges : function(){
21866         var m = this.modified.slice(0);
21867         this.modified = [];
21868         for(var i = 0, len = m.length; i < len; i++){
21869             m[i].reject();
21870         }
21871     },
21872
21873     onMetaChange : function(meta, rtype, o){
21874         this.recordType = rtype;
21875         this.fields = rtype.prototype.fields;
21876         delete this.snapshot;
21877         this.sortInfo = meta.sortInfo || this.sortInfo;
21878         this.modified = [];
21879         this.fireEvent('metachange', this, this.reader.meta);
21880     },
21881     
21882     moveIndex : function(data, type)
21883     {
21884         var index = this.indexOf(data);
21885         
21886         var newIndex = index + type;
21887         
21888         this.remove(data);
21889         
21890         this.insert(newIndex, data);
21891         
21892     }
21893 });/*
21894  * Based on:
21895  * Ext JS Library 1.1.1
21896  * Copyright(c) 2006-2007, Ext JS, LLC.
21897  *
21898  * Originally Released Under LGPL - original licence link has changed is not relivant.
21899  *
21900  * Fork - LGPL
21901  * <script type="text/javascript">
21902  */
21903
21904 /**
21905  * @class Roo.data.SimpleStore
21906  * @extends Roo.data.Store
21907  * Small helper class to make creating Stores from Array data easier.
21908  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21909  * @cfg {Array} fields An array of field definition objects, or field name strings.
21910  * @cfg {Array} data The multi-dimensional array of data
21911  * @constructor
21912  * @param {Object} config
21913  */
21914 Roo.data.SimpleStore = function(config){
21915     Roo.data.SimpleStore.superclass.constructor.call(this, {
21916         isLocal : true,
21917         reader: new Roo.data.ArrayReader({
21918                 id: config.id
21919             },
21920             Roo.data.Record.create(config.fields)
21921         ),
21922         proxy : new Roo.data.MemoryProxy(config.data)
21923     });
21924     this.load();
21925 };
21926 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21927  * Based on:
21928  * Ext JS Library 1.1.1
21929  * Copyright(c) 2006-2007, Ext JS, LLC.
21930  *
21931  * Originally Released Under LGPL - original licence link has changed is not relivant.
21932  *
21933  * Fork - LGPL
21934  * <script type="text/javascript">
21935  */
21936
21937 /**
21938 /**
21939  * @extends Roo.data.Store
21940  * @class Roo.data.JsonStore
21941  * Small helper class to make creating Stores for JSON data easier. <br/>
21942 <pre><code>
21943 var store = new Roo.data.JsonStore({
21944     url: 'get-images.php',
21945     root: 'images',
21946     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21947 });
21948 </code></pre>
21949  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21950  * JsonReader and HttpProxy (unless inline data is provided).</b>
21951  * @cfg {Array} fields An array of field definition objects, or field name strings.
21952  * @constructor
21953  * @param {Object} config
21954  */
21955 Roo.data.JsonStore = function(c){
21956     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21957         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21958         reader: new Roo.data.JsonReader(c, c.fields)
21959     }));
21960 };
21961 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21962  * Based on:
21963  * Ext JS Library 1.1.1
21964  * Copyright(c) 2006-2007, Ext JS, LLC.
21965  *
21966  * Originally Released Under LGPL - original licence link has changed is not relivant.
21967  *
21968  * Fork - LGPL
21969  * <script type="text/javascript">
21970  */
21971
21972  
21973 Roo.data.Field = function(config){
21974     if(typeof config == "string"){
21975         config = {name: config};
21976     }
21977     Roo.apply(this, config);
21978     
21979     if(!this.type){
21980         this.type = "auto";
21981     }
21982     
21983     var st = Roo.data.SortTypes;
21984     // named sortTypes are supported, here we look them up
21985     if(typeof this.sortType == "string"){
21986         this.sortType = st[this.sortType];
21987     }
21988     
21989     // set default sortType for strings and dates
21990     if(!this.sortType){
21991         switch(this.type){
21992             case "string":
21993                 this.sortType = st.asUCString;
21994                 break;
21995             case "date":
21996                 this.sortType = st.asDate;
21997                 break;
21998             default:
21999                 this.sortType = st.none;
22000         }
22001     }
22002
22003     // define once
22004     var stripRe = /[\$,%]/g;
22005
22006     // prebuilt conversion function for this field, instead of
22007     // switching every time we're reading a value
22008     if(!this.convert){
22009         var cv, dateFormat = this.dateFormat;
22010         switch(this.type){
22011             case "":
22012             case "auto":
22013             case undefined:
22014                 cv = function(v){ return v; };
22015                 break;
22016             case "string":
22017                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22018                 break;
22019             case "int":
22020                 cv = function(v){
22021                     return v !== undefined && v !== null && v !== '' ?
22022                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22023                     };
22024                 break;
22025             case "float":
22026                 cv = function(v){
22027                     return v !== undefined && v !== null && v !== '' ?
22028                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22029                     };
22030                 break;
22031             case "bool":
22032             case "boolean":
22033                 cv = function(v){ return v === true || v === "true" || v == 1; };
22034                 break;
22035             case "date":
22036                 cv = function(v){
22037                     if(!v){
22038                         return '';
22039                     }
22040                     if(v instanceof Date){
22041                         return v;
22042                     }
22043                     if(dateFormat){
22044                         if(dateFormat == "timestamp"){
22045                             return new Date(v*1000);
22046                         }
22047                         return Date.parseDate(v, dateFormat);
22048                     }
22049                     var parsed = Date.parse(v);
22050                     return parsed ? new Date(parsed) : null;
22051                 };
22052              break;
22053             
22054         }
22055         this.convert = cv;
22056     }
22057 };
22058
22059 Roo.data.Field.prototype = {
22060     dateFormat: null,
22061     defaultValue: "",
22062     mapping: null,
22063     sortType : null,
22064     sortDir : "ASC"
22065 };/*
22066  * Based on:
22067  * Ext JS Library 1.1.1
22068  * Copyright(c) 2006-2007, Ext JS, LLC.
22069  *
22070  * Originally Released Under LGPL - original licence link has changed is not relivant.
22071  *
22072  * Fork - LGPL
22073  * <script type="text/javascript">
22074  */
22075  
22076 // Base class for reading structured data from a data source.  This class is intended to be
22077 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22078
22079 /**
22080  * @class Roo.data.DataReader
22081  * Base class for reading structured data from a data source.  This class is intended to be
22082  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22083  */
22084
22085 Roo.data.DataReader = function(meta, recordType){
22086     
22087     this.meta = meta;
22088     
22089     this.recordType = recordType instanceof Array ? 
22090         Roo.data.Record.create(recordType) : recordType;
22091 };
22092
22093 Roo.data.DataReader.prototype = {
22094      /**
22095      * Create an empty record
22096      * @param {Object} data (optional) - overlay some values
22097      * @return {Roo.data.Record} record created.
22098      */
22099     newRow :  function(d) {
22100         var da =  {};
22101         this.recordType.prototype.fields.each(function(c) {
22102             switch( c.type) {
22103                 case 'int' : da[c.name] = 0; break;
22104                 case 'date' : da[c.name] = new Date(); break;
22105                 case 'float' : da[c.name] = 0.0; break;
22106                 case 'boolean' : da[c.name] = false; break;
22107                 default : da[c.name] = ""; break;
22108             }
22109             
22110         });
22111         return new this.recordType(Roo.apply(da, d));
22112     }
22113     
22114 };/*
22115  * Based on:
22116  * Ext JS Library 1.1.1
22117  * Copyright(c) 2006-2007, Ext JS, LLC.
22118  *
22119  * Originally Released Under LGPL - original licence link has changed is not relivant.
22120  *
22121  * Fork - LGPL
22122  * <script type="text/javascript">
22123  */
22124
22125 /**
22126  * @class Roo.data.DataProxy
22127  * @extends Roo.data.Observable
22128  * This class is an abstract base class for implementations which provide retrieval of
22129  * unformatted data objects.<br>
22130  * <p>
22131  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22132  * (of the appropriate type which knows how to parse the data object) to provide a block of
22133  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22134  * <p>
22135  * Custom implementations must implement the load method as described in
22136  * {@link Roo.data.HttpProxy#load}.
22137  */
22138 Roo.data.DataProxy = function(){
22139     this.addEvents({
22140         /**
22141          * @event beforeload
22142          * Fires before a network request is made to retrieve a data object.
22143          * @param {Object} This DataProxy object.
22144          * @param {Object} params The params parameter to the load function.
22145          */
22146         beforeload : true,
22147         /**
22148          * @event load
22149          * Fires before the load method's callback is called.
22150          * @param {Object} This DataProxy object.
22151          * @param {Object} o The data object.
22152          * @param {Object} arg The callback argument object passed to the load function.
22153          */
22154         load : true,
22155         /**
22156          * @event loadexception
22157          * Fires if an Exception occurs during data retrieval.
22158          * @param {Object} This DataProxy object.
22159          * @param {Object} o The data object.
22160          * @param {Object} arg The callback argument object passed to the load function.
22161          * @param {Object} e The Exception.
22162          */
22163         loadexception : true
22164     });
22165     Roo.data.DataProxy.superclass.constructor.call(this);
22166 };
22167
22168 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22169
22170     /**
22171      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22172      */
22173 /*
22174  * Based on:
22175  * Ext JS Library 1.1.1
22176  * Copyright(c) 2006-2007, Ext JS, LLC.
22177  *
22178  * Originally Released Under LGPL - original licence link has changed is not relivant.
22179  *
22180  * Fork - LGPL
22181  * <script type="text/javascript">
22182  */
22183 /**
22184  * @class Roo.data.MemoryProxy
22185  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22186  * to the Reader when its load method is called.
22187  * @constructor
22188  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22189  */
22190 Roo.data.MemoryProxy = function(data){
22191     if (data.data) {
22192         data = data.data;
22193     }
22194     Roo.data.MemoryProxy.superclass.constructor.call(this);
22195     this.data = data;
22196 };
22197
22198 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22199     /**
22200      * Load data from the requested source (in this case an in-memory
22201      * data object passed to the constructor), read the data object into
22202      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22203      * process that block using the passed callback.
22204      * @param {Object} params This parameter is not used by the MemoryProxy class.
22205      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22206      * object into a block of Roo.data.Records.
22207      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22208      * The function must be passed <ul>
22209      * <li>The Record block object</li>
22210      * <li>The "arg" argument from the load function</li>
22211      * <li>A boolean success indicator</li>
22212      * </ul>
22213      * @param {Object} scope The scope in which to call the callback
22214      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22215      */
22216     load : function(params, reader, callback, scope, arg){
22217         params = params || {};
22218         var result;
22219         try {
22220             result = reader.readRecords(this.data);
22221         }catch(e){
22222             this.fireEvent("loadexception", this, arg, null, e);
22223             callback.call(scope, null, arg, false);
22224             return;
22225         }
22226         callback.call(scope, result, arg, true);
22227     },
22228     
22229     // private
22230     update : function(params, records){
22231         
22232     }
22233 });/*
22234  * Based on:
22235  * Ext JS Library 1.1.1
22236  * Copyright(c) 2006-2007, Ext JS, LLC.
22237  *
22238  * Originally Released Under LGPL - original licence link has changed is not relivant.
22239  *
22240  * Fork - LGPL
22241  * <script type="text/javascript">
22242  */
22243 /**
22244  * @class Roo.data.HttpProxy
22245  * @extends Roo.data.DataProxy
22246  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22247  * configured to reference a certain URL.<br><br>
22248  * <p>
22249  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22250  * from which the running page was served.<br><br>
22251  * <p>
22252  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22253  * <p>
22254  * Be aware that to enable the browser to parse an XML document, the server must set
22255  * the Content-Type header in the HTTP response to "text/xml".
22256  * @constructor
22257  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22258  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22259  * will be used to make the request.
22260  */
22261 Roo.data.HttpProxy = function(conn){
22262     Roo.data.HttpProxy.superclass.constructor.call(this);
22263     // is conn a conn config or a real conn?
22264     this.conn = conn;
22265     this.useAjax = !conn || !conn.events;
22266   
22267 };
22268
22269 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22270     // thse are take from connection...
22271     
22272     /**
22273      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22274      */
22275     /**
22276      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22277      * extra parameters to each request made by this object. (defaults to undefined)
22278      */
22279     /**
22280      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22281      *  to each request made by this object. (defaults to undefined)
22282      */
22283     /**
22284      * @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)
22285      */
22286     /**
22287      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22288      */
22289      /**
22290      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22291      * @type Boolean
22292      */
22293   
22294
22295     /**
22296      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22297      * @type Boolean
22298      */
22299     /**
22300      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22301      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22302      * a finer-grained basis than the DataProxy events.
22303      */
22304     getConnection : function(){
22305         return this.useAjax ? Roo.Ajax : this.conn;
22306     },
22307
22308     /**
22309      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22310      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22311      * process that block using the passed callback.
22312      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22313      * for the request to the remote server.
22314      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22315      * object into a block of Roo.data.Records.
22316      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22317      * The function must be passed <ul>
22318      * <li>The Record block object</li>
22319      * <li>The "arg" argument from the load function</li>
22320      * <li>A boolean success indicator</li>
22321      * </ul>
22322      * @param {Object} scope The scope in which to call the callback
22323      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22324      */
22325     load : function(params, reader, callback, scope, arg){
22326         if(this.fireEvent("beforeload", this, params) !== false){
22327             var  o = {
22328                 params : params || {},
22329                 request: {
22330                     callback : callback,
22331                     scope : scope,
22332                     arg : arg
22333                 },
22334                 reader: reader,
22335                 callback : this.loadResponse,
22336                 scope: this
22337             };
22338             if(this.useAjax){
22339                 Roo.applyIf(o, this.conn);
22340                 if(this.activeRequest){
22341                     Roo.Ajax.abort(this.activeRequest);
22342                 }
22343                 this.activeRequest = Roo.Ajax.request(o);
22344             }else{
22345                 this.conn.request(o);
22346             }
22347         }else{
22348             callback.call(scope||this, null, arg, false);
22349         }
22350     },
22351
22352     // private
22353     loadResponse : function(o, success, response){
22354         delete this.activeRequest;
22355         if(!success){
22356             this.fireEvent("loadexception", this, o, response);
22357             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22358             return;
22359         }
22360         var result;
22361         try {
22362             result = o.reader.read(response);
22363         }catch(e){
22364             this.fireEvent("loadexception", this, o, response, e);
22365             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22366             return;
22367         }
22368         
22369         this.fireEvent("load", this, o, o.request.arg);
22370         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22371     },
22372
22373     // private
22374     update : function(dataSet){
22375
22376     },
22377
22378     // private
22379     updateResponse : function(dataSet){
22380
22381     }
22382 });/*
22383  * Based on:
22384  * Ext JS Library 1.1.1
22385  * Copyright(c) 2006-2007, Ext JS, LLC.
22386  *
22387  * Originally Released Under LGPL - original licence link has changed is not relivant.
22388  *
22389  * Fork - LGPL
22390  * <script type="text/javascript">
22391  */
22392
22393 /**
22394  * @class Roo.data.ScriptTagProxy
22395  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22396  * other than the originating domain of the running page.<br><br>
22397  * <p>
22398  * <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
22399  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22400  * <p>
22401  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22402  * source code that is used as the source inside a &lt;script> tag.<br><br>
22403  * <p>
22404  * In order for the browser to process the returned data, the server must wrap the data object
22405  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22406  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22407  * depending on whether the callback name was passed:
22408  * <p>
22409  * <pre><code>
22410 boolean scriptTag = false;
22411 String cb = request.getParameter("callback");
22412 if (cb != null) {
22413     scriptTag = true;
22414     response.setContentType("text/javascript");
22415 } else {
22416     response.setContentType("application/x-json");
22417 }
22418 Writer out = response.getWriter();
22419 if (scriptTag) {
22420     out.write(cb + "(");
22421 }
22422 out.print(dataBlock.toJsonString());
22423 if (scriptTag) {
22424     out.write(");");
22425 }
22426 </pre></code>
22427  *
22428  * @constructor
22429  * @param {Object} config A configuration object.
22430  */
22431 Roo.data.ScriptTagProxy = function(config){
22432     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22433     Roo.apply(this, config);
22434     this.head = document.getElementsByTagName("head")[0];
22435 };
22436
22437 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22438
22439 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22440     /**
22441      * @cfg {String} url The URL from which to request the data object.
22442      */
22443     /**
22444      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22445      */
22446     timeout : 30000,
22447     /**
22448      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22449      * the server the name of the callback function set up by the load call to process the returned data object.
22450      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22451      * javascript output which calls this named function passing the data object as its only parameter.
22452      */
22453     callbackParam : "callback",
22454     /**
22455      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22456      * name to the request.
22457      */
22458     nocache : true,
22459
22460     /**
22461      * Load data from the configured URL, read the data object into
22462      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22463      * process that block using the passed callback.
22464      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22465      * for the request to the remote server.
22466      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22467      * object into a block of Roo.data.Records.
22468      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22469      * The function must be passed <ul>
22470      * <li>The Record block object</li>
22471      * <li>The "arg" argument from the load function</li>
22472      * <li>A boolean success indicator</li>
22473      * </ul>
22474      * @param {Object} scope The scope in which to call the callback
22475      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22476      */
22477     load : function(params, reader, callback, scope, arg){
22478         if(this.fireEvent("beforeload", this, params) !== false){
22479
22480             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22481
22482             var url = this.url;
22483             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22484             if(this.nocache){
22485                 url += "&_dc=" + (new Date().getTime());
22486             }
22487             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22488             var trans = {
22489                 id : transId,
22490                 cb : "stcCallback"+transId,
22491                 scriptId : "stcScript"+transId,
22492                 params : params,
22493                 arg : arg,
22494                 url : url,
22495                 callback : callback,
22496                 scope : scope,
22497                 reader : reader
22498             };
22499             var conn = this;
22500
22501             window[trans.cb] = function(o){
22502                 conn.handleResponse(o, trans);
22503             };
22504
22505             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22506
22507             if(this.autoAbort !== false){
22508                 this.abort();
22509             }
22510
22511             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22512
22513             var script = document.createElement("script");
22514             script.setAttribute("src", url);
22515             script.setAttribute("type", "text/javascript");
22516             script.setAttribute("id", trans.scriptId);
22517             this.head.appendChild(script);
22518
22519             this.trans = trans;
22520         }else{
22521             callback.call(scope||this, null, arg, false);
22522         }
22523     },
22524
22525     // private
22526     isLoading : function(){
22527         return this.trans ? true : false;
22528     },
22529
22530     /**
22531      * Abort the current server request.
22532      */
22533     abort : function(){
22534         if(this.isLoading()){
22535             this.destroyTrans(this.trans);
22536         }
22537     },
22538
22539     // private
22540     destroyTrans : function(trans, isLoaded){
22541         this.head.removeChild(document.getElementById(trans.scriptId));
22542         clearTimeout(trans.timeoutId);
22543         if(isLoaded){
22544             window[trans.cb] = undefined;
22545             try{
22546                 delete window[trans.cb];
22547             }catch(e){}
22548         }else{
22549             // if hasn't been loaded, wait for load to remove it to prevent script error
22550             window[trans.cb] = function(){
22551                 window[trans.cb] = undefined;
22552                 try{
22553                     delete window[trans.cb];
22554                 }catch(e){}
22555             };
22556         }
22557     },
22558
22559     // private
22560     handleResponse : function(o, trans){
22561         this.trans = false;
22562         this.destroyTrans(trans, true);
22563         var result;
22564         try {
22565             result = trans.reader.readRecords(o);
22566         }catch(e){
22567             this.fireEvent("loadexception", this, o, trans.arg, e);
22568             trans.callback.call(trans.scope||window, null, trans.arg, false);
22569             return;
22570         }
22571         this.fireEvent("load", this, o, trans.arg);
22572         trans.callback.call(trans.scope||window, result, trans.arg, true);
22573     },
22574
22575     // private
22576     handleFailure : function(trans){
22577         this.trans = false;
22578         this.destroyTrans(trans, false);
22579         this.fireEvent("loadexception", this, null, trans.arg);
22580         trans.callback.call(trans.scope||window, null, trans.arg, false);
22581     }
22582 });/*
22583  * Based on:
22584  * Ext JS Library 1.1.1
22585  * Copyright(c) 2006-2007, Ext JS, LLC.
22586  *
22587  * Originally Released Under LGPL - original licence link has changed is not relivant.
22588  *
22589  * Fork - LGPL
22590  * <script type="text/javascript">
22591  */
22592
22593 /**
22594  * @class Roo.data.JsonReader
22595  * @extends Roo.data.DataReader
22596  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22597  * based on mappings in a provided Roo.data.Record constructor.
22598  * 
22599  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22600  * in the reply previously. 
22601  * 
22602  * <p>
22603  * Example code:
22604  * <pre><code>
22605 var RecordDef = Roo.data.Record.create([
22606     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22607     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22608 ]);
22609 var myReader = new Roo.data.JsonReader({
22610     totalProperty: "results",    // The property which contains the total dataset size (optional)
22611     root: "rows",                // The property which contains an Array of row objects
22612     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22613 }, RecordDef);
22614 </code></pre>
22615  * <p>
22616  * This would consume a JSON file like this:
22617  * <pre><code>
22618 { 'results': 2, 'rows': [
22619     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22620     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22621 }
22622 </code></pre>
22623  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22624  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22625  * paged from the remote server.
22626  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22627  * @cfg {String} root name of the property which contains the Array of row objects.
22628  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22629  * @cfg {Array} fields Array of field definition objects
22630  * @constructor
22631  * Create a new JsonReader
22632  * @param {Object} meta Metadata configuration options
22633  * @param {Object} recordType Either an Array of field definition objects,
22634  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22635  */
22636 Roo.data.JsonReader = function(meta, recordType){
22637     
22638     meta = meta || {};
22639     // set some defaults:
22640     Roo.applyIf(meta, {
22641         totalProperty: 'total',
22642         successProperty : 'success',
22643         root : 'data',
22644         id : 'id'
22645     });
22646     
22647     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22648 };
22649 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22650     
22651     /**
22652      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22653      * Used by Store query builder to append _requestMeta to params.
22654      * 
22655      */
22656     metaFromRemote : false,
22657     /**
22658      * This method is only used by a DataProxy which has retrieved data from a remote server.
22659      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22660      * @return {Object} data A data block which is used by an Roo.data.Store object as
22661      * a cache of Roo.data.Records.
22662      */
22663     read : function(response){
22664         var json = response.responseText;
22665        
22666         var o = /* eval:var:o */ eval("("+json+")");
22667         if(!o) {
22668             throw {message: "JsonReader.read: Json object not found"};
22669         }
22670         
22671         if(o.metaData){
22672             
22673             delete this.ef;
22674             this.metaFromRemote = true;
22675             this.meta = o.metaData;
22676             this.recordType = Roo.data.Record.create(o.metaData.fields);
22677             this.onMetaChange(this.meta, this.recordType, o);
22678         }
22679         return this.readRecords(o);
22680     },
22681
22682     // private function a store will implement
22683     onMetaChange : function(meta, recordType, o){
22684
22685     },
22686
22687     /**
22688          * @ignore
22689          */
22690     simpleAccess: function(obj, subsc) {
22691         return obj[subsc];
22692     },
22693
22694         /**
22695          * @ignore
22696          */
22697     getJsonAccessor: function(){
22698         var re = /[\[\.]/;
22699         return function(expr) {
22700             try {
22701                 return(re.test(expr))
22702                     ? new Function("obj", "return obj." + expr)
22703                     : function(obj){
22704                         return obj[expr];
22705                     };
22706             } catch(e){}
22707             return Roo.emptyFn;
22708         };
22709     }(),
22710
22711     /**
22712      * Create a data block containing Roo.data.Records from an XML document.
22713      * @param {Object} o An object which contains an Array of row objects in the property specified
22714      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22715      * which contains the total size of the dataset.
22716      * @return {Object} data A data block which is used by an Roo.data.Store object as
22717      * a cache of Roo.data.Records.
22718      */
22719     readRecords : function(o){
22720         /**
22721          * After any data loads, the raw JSON data is available for further custom processing.
22722          * @type Object
22723          */
22724         this.o = o;
22725         var s = this.meta, Record = this.recordType,
22726             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22727
22728 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22729         if (!this.ef) {
22730             if(s.totalProperty) {
22731                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22732                 }
22733                 if(s.successProperty) {
22734                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22735                 }
22736                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22737                 if (s.id) {
22738                         var g = this.getJsonAccessor(s.id);
22739                         this.getId = function(rec) {
22740                                 var r = g(rec);  
22741                                 return (r === undefined || r === "") ? null : r;
22742                         };
22743                 } else {
22744                         this.getId = function(){return null;};
22745                 }
22746             this.ef = [];
22747             for(var jj = 0; jj < fl; jj++){
22748                 f = fi[jj];
22749                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22750                 this.ef[jj] = this.getJsonAccessor(map);
22751             }
22752         }
22753
22754         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22755         if(s.totalProperty){
22756             var vt = parseInt(this.getTotal(o), 10);
22757             if(!isNaN(vt)){
22758                 totalRecords = vt;
22759             }
22760         }
22761         if(s.successProperty){
22762             var vs = this.getSuccess(o);
22763             if(vs === false || vs === 'false'){
22764                 success = false;
22765             }
22766         }
22767         var records = [];
22768         for(var i = 0; i < c; i++){
22769                 var n = root[i];
22770             var values = {};
22771             var id = this.getId(n);
22772             for(var j = 0; j < fl; j++){
22773                 f = fi[j];
22774             var v = this.ef[j](n);
22775             if (!f.convert) {
22776                 Roo.log('missing convert for ' + f.name);
22777                 Roo.log(f);
22778                 continue;
22779             }
22780             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22781             }
22782             var record = new Record(values, id);
22783             record.json = n;
22784             records[i] = record;
22785         }
22786         return {
22787             raw : o,
22788             success : success,
22789             records : records,
22790             totalRecords : totalRecords
22791         };
22792     }
22793 });/*
22794  * Based on:
22795  * Ext JS Library 1.1.1
22796  * Copyright(c) 2006-2007, Ext JS, LLC.
22797  *
22798  * Originally Released Under LGPL - original licence link has changed is not relivant.
22799  *
22800  * Fork - LGPL
22801  * <script type="text/javascript">
22802  */
22803
22804 /**
22805  * @class Roo.data.XmlReader
22806  * @extends Roo.data.DataReader
22807  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22808  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22809  * <p>
22810  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22811  * header in the HTTP response must be set to "text/xml".</em>
22812  * <p>
22813  * Example code:
22814  * <pre><code>
22815 var RecordDef = Roo.data.Record.create([
22816    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22817    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22818 ]);
22819 var myReader = new Roo.data.XmlReader({
22820    totalRecords: "results", // The element which contains the total dataset size (optional)
22821    record: "row",           // The repeated element which contains row information
22822    id: "id"                 // The element within the row that provides an ID for the record (optional)
22823 }, RecordDef);
22824 </code></pre>
22825  * <p>
22826  * This would consume an XML file like this:
22827  * <pre><code>
22828 &lt;?xml?>
22829 &lt;dataset>
22830  &lt;results>2&lt;/results>
22831  &lt;row>
22832    &lt;id>1&lt;/id>
22833    &lt;name>Bill&lt;/name>
22834    &lt;occupation>Gardener&lt;/occupation>
22835  &lt;/row>
22836  &lt;row>
22837    &lt;id>2&lt;/id>
22838    &lt;name>Ben&lt;/name>
22839    &lt;occupation>Horticulturalist&lt;/occupation>
22840  &lt;/row>
22841 &lt;/dataset>
22842 </code></pre>
22843  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22844  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22845  * paged from the remote server.
22846  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22847  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22848  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22849  * a record identifier value.
22850  * @constructor
22851  * Create a new XmlReader
22852  * @param {Object} meta Metadata configuration options
22853  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22854  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22855  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22856  */
22857 Roo.data.XmlReader = function(meta, recordType){
22858     meta = meta || {};
22859     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22860 };
22861 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22862     /**
22863      * This method is only used by a DataProxy which has retrieved data from a remote server.
22864          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22865          * to contain a method called 'responseXML' that returns an XML document object.
22866      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22867      * a cache of Roo.data.Records.
22868      */
22869     read : function(response){
22870         var doc = response.responseXML;
22871         if(!doc) {
22872             throw {message: "XmlReader.read: XML Document not available"};
22873         }
22874         return this.readRecords(doc);
22875     },
22876
22877     /**
22878      * Create a data block containing Roo.data.Records from an XML document.
22879          * @param {Object} doc A parsed XML document.
22880      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22881      * a cache of Roo.data.Records.
22882      */
22883     readRecords : function(doc){
22884         /**
22885          * After any data loads/reads, the raw XML Document is available for further custom processing.
22886          * @type XMLDocument
22887          */
22888         this.xmlData = doc;
22889         var root = doc.documentElement || doc;
22890         var q = Roo.DomQuery;
22891         var recordType = this.recordType, fields = recordType.prototype.fields;
22892         var sid = this.meta.id;
22893         var totalRecords = 0, success = true;
22894         if(this.meta.totalRecords){
22895             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22896         }
22897         
22898         if(this.meta.success){
22899             var sv = q.selectValue(this.meta.success, root, true);
22900             success = sv !== false && sv !== 'false';
22901         }
22902         var records = [];
22903         var ns = q.select(this.meta.record, root);
22904         for(var i = 0, len = ns.length; i < len; i++) {
22905                 var n = ns[i];
22906                 var values = {};
22907                 var id = sid ? q.selectValue(sid, n) : undefined;
22908                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22909                     var f = fields.items[j];
22910                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22911                     v = f.convert(v);
22912                     values[f.name] = v;
22913                 }
22914                 var record = new recordType(values, id);
22915                 record.node = n;
22916                 records[records.length] = record;
22917             }
22918
22919             return {
22920                 success : success,
22921                 records : records,
22922                 totalRecords : totalRecords || records.length
22923             };
22924     }
22925 });/*
22926  * Based on:
22927  * Ext JS Library 1.1.1
22928  * Copyright(c) 2006-2007, Ext JS, LLC.
22929  *
22930  * Originally Released Under LGPL - original licence link has changed is not relivant.
22931  *
22932  * Fork - LGPL
22933  * <script type="text/javascript">
22934  */
22935
22936 /**
22937  * @class Roo.data.ArrayReader
22938  * @extends Roo.data.DataReader
22939  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22940  * Each element of that Array represents a row of data fields. The
22941  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22942  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22943  * <p>
22944  * Example code:.
22945  * <pre><code>
22946 var RecordDef = Roo.data.Record.create([
22947     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22948     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22949 ]);
22950 var myReader = new Roo.data.ArrayReader({
22951     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22952 }, RecordDef);
22953 </code></pre>
22954  * <p>
22955  * This would consume an Array like this:
22956  * <pre><code>
22957 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22958   </code></pre>
22959  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22960  * @constructor
22961  * Create a new JsonReader
22962  * @param {Object} meta Metadata configuration options.
22963  * @param {Object} recordType Either an Array of field definition objects
22964  * as specified to {@link Roo.data.Record#create},
22965  * or an {@link Roo.data.Record} object
22966  * created using {@link Roo.data.Record#create}.
22967  */
22968 Roo.data.ArrayReader = function(meta, recordType){
22969     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22970 };
22971
22972 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22973     /**
22974      * Create a data block containing Roo.data.Records from an XML document.
22975      * @param {Object} o An Array of row objects which represents the dataset.
22976      * @return {Object} data A data block which is used by an Roo.data.Store object as
22977      * a cache of Roo.data.Records.
22978      */
22979     readRecords : function(o){
22980         var sid = this.meta ? this.meta.id : null;
22981         var recordType = this.recordType, fields = recordType.prototype.fields;
22982         var records = [];
22983         var root = o;
22984             for(var i = 0; i < root.length; i++){
22985                     var n = root[i];
22986                 var values = {};
22987                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22988                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22989                 var f = fields.items[j];
22990                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22991                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22992                 v = f.convert(v);
22993                 values[f.name] = v;
22994             }
22995                 var record = new recordType(values, id);
22996                 record.json = n;
22997                 records[records.length] = record;
22998             }
22999             return {
23000                 records : records,
23001                 totalRecords : records.length
23002             };
23003     }
23004 });/*
23005  * Based on:
23006  * Ext JS Library 1.1.1
23007  * Copyright(c) 2006-2007, Ext JS, LLC.
23008  *
23009  * Originally Released Under LGPL - original licence link has changed is not relivant.
23010  *
23011  * Fork - LGPL
23012  * <script type="text/javascript">
23013  */
23014
23015
23016 /**
23017  * @class Roo.data.Tree
23018  * @extends Roo.util.Observable
23019  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23020  * in the tree have most standard DOM functionality.
23021  * @constructor
23022  * @param {Node} root (optional) The root node
23023  */
23024 Roo.data.Tree = function(root){
23025    this.nodeHash = {};
23026    /**
23027     * The root node for this tree
23028     * @type Node
23029     */
23030    this.root = null;
23031    if(root){
23032        this.setRootNode(root);
23033    }
23034    this.addEvents({
23035        /**
23036         * @event append
23037         * Fires when a new child node is appended to a node in this tree.
23038         * @param {Tree} tree The owner tree
23039         * @param {Node} parent The parent node
23040         * @param {Node} node The newly appended node
23041         * @param {Number} index The index of the newly appended node
23042         */
23043        "append" : true,
23044        /**
23045         * @event remove
23046         * Fires when a child node is removed from a node in this tree.
23047         * @param {Tree} tree The owner tree
23048         * @param {Node} parent The parent node
23049         * @param {Node} node The child node removed
23050         */
23051        "remove" : true,
23052        /**
23053         * @event move
23054         * Fires when a node is moved to a new location in the tree
23055         * @param {Tree} tree The owner tree
23056         * @param {Node} node The node moved
23057         * @param {Node} oldParent The old parent of this node
23058         * @param {Node} newParent The new parent of this node
23059         * @param {Number} index The index it was moved to
23060         */
23061        "move" : true,
23062        /**
23063         * @event insert
23064         * Fires when a new child node is inserted in a node in this tree.
23065         * @param {Tree} tree The owner tree
23066         * @param {Node} parent The parent node
23067         * @param {Node} node The child node inserted
23068         * @param {Node} refNode The child node the node was inserted before
23069         */
23070        "insert" : true,
23071        /**
23072         * @event beforeappend
23073         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23074         * @param {Tree} tree The owner tree
23075         * @param {Node} parent The parent node
23076         * @param {Node} node The child node to be appended
23077         */
23078        "beforeappend" : true,
23079        /**
23080         * @event beforeremove
23081         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23082         * @param {Tree} tree The owner tree
23083         * @param {Node} parent The parent node
23084         * @param {Node} node The child node to be removed
23085         */
23086        "beforeremove" : true,
23087        /**
23088         * @event beforemove
23089         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23090         * @param {Tree} tree The owner tree
23091         * @param {Node} node The node being moved
23092         * @param {Node} oldParent The parent of the node
23093         * @param {Node} newParent The new parent the node is moving to
23094         * @param {Number} index The index it is being moved to
23095         */
23096        "beforemove" : true,
23097        /**
23098         * @event beforeinsert
23099         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23100         * @param {Tree} tree The owner tree
23101         * @param {Node} parent The parent node
23102         * @param {Node} node The child node to be inserted
23103         * @param {Node} refNode The child node the node is being inserted before
23104         */
23105        "beforeinsert" : true
23106    });
23107
23108     Roo.data.Tree.superclass.constructor.call(this);
23109 };
23110
23111 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23112     pathSeparator: "/",
23113
23114     proxyNodeEvent : function(){
23115         return this.fireEvent.apply(this, arguments);
23116     },
23117
23118     /**
23119      * Returns the root node for this tree.
23120      * @return {Node}
23121      */
23122     getRootNode : function(){
23123         return this.root;
23124     },
23125
23126     /**
23127      * Sets the root node for this tree.
23128      * @param {Node} node
23129      * @return {Node}
23130      */
23131     setRootNode : function(node){
23132         this.root = node;
23133         node.ownerTree = this;
23134         node.isRoot = true;
23135         this.registerNode(node);
23136         return node;
23137     },
23138
23139     /**
23140      * Gets a node in this tree by its id.
23141      * @param {String} id
23142      * @return {Node}
23143      */
23144     getNodeById : function(id){
23145         return this.nodeHash[id];
23146     },
23147
23148     registerNode : function(node){
23149         this.nodeHash[node.id] = node;
23150     },
23151
23152     unregisterNode : function(node){
23153         delete this.nodeHash[node.id];
23154     },
23155
23156     toString : function(){
23157         return "[Tree"+(this.id?" "+this.id:"")+"]";
23158     }
23159 });
23160
23161 /**
23162  * @class Roo.data.Node
23163  * @extends Roo.util.Observable
23164  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23165  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23166  * @constructor
23167  * @param {Object} attributes The attributes/config for the node
23168  */
23169 Roo.data.Node = function(attributes){
23170     /**
23171      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23172      * @type {Object}
23173      */
23174     this.attributes = attributes || {};
23175     this.leaf = this.attributes.leaf;
23176     /**
23177      * The node id. @type String
23178      */
23179     this.id = this.attributes.id;
23180     if(!this.id){
23181         this.id = Roo.id(null, "ynode-");
23182         this.attributes.id = this.id;
23183     }
23184      
23185     
23186     /**
23187      * All child nodes of this node. @type Array
23188      */
23189     this.childNodes = [];
23190     if(!this.childNodes.indexOf){ // indexOf is a must
23191         this.childNodes.indexOf = function(o){
23192             for(var i = 0, len = this.length; i < len; i++){
23193                 if(this[i] == o) {
23194                     return i;
23195                 }
23196             }
23197             return -1;
23198         };
23199     }
23200     /**
23201      * The parent node for this node. @type Node
23202      */
23203     this.parentNode = null;
23204     /**
23205      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23206      */
23207     this.firstChild = null;
23208     /**
23209      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23210      */
23211     this.lastChild = null;
23212     /**
23213      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23214      */
23215     this.previousSibling = null;
23216     /**
23217      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23218      */
23219     this.nextSibling = null;
23220
23221     this.addEvents({
23222        /**
23223         * @event append
23224         * Fires when a new child node is appended
23225         * @param {Tree} tree The owner tree
23226         * @param {Node} this This node
23227         * @param {Node} node The newly appended node
23228         * @param {Number} index The index of the newly appended node
23229         */
23230        "append" : true,
23231        /**
23232         * @event remove
23233         * Fires when a child node is removed
23234         * @param {Tree} tree The owner tree
23235         * @param {Node} this This node
23236         * @param {Node} node The removed node
23237         */
23238        "remove" : true,
23239        /**
23240         * @event move
23241         * Fires when this node is moved to a new location in the tree
23242         * @param {Tree} tree The owner tree
23243         * @param {Node} this This node
23244         * @param {Node} oldParent The old parent of this node
23245         * @param {Node} newParent The new parent of this node
23246         * @param {Number} index The index it was moved to
23247         */
23248        "move" : true,
23249        /**
23250         * @event insert
23251         * Fires when a new child node is inserted.
23252         * @param {Tree} tree The owner tree
23253         * @param {Node} this This node
23254         * @param {Node} node The child node inserted
23255         * @param {Node} refNode The child node the node was inserted before
23256         */
23257        "insert" : true,
23258        /**
23259         * @event beforeappend
23260         * Fires before a new child is appended, return false to cancel the append.
23261         * @param {Tree} tree The owner tree
23262         * @param {Node} this This node
23263         * @param {Node} node The child node to be appended
23264         */
23265        "beforeappend" : true,
23266        /**
23267         * @event beforeremove
23268         * Fires before a child is removed, return false to cancel the remove.
23269         * @param {Tree} tree The owner tree
23270         * @param {Node} this This node
23271         * @param {Node} node The child node to be removed
23272         */
23273        "beforeremove" : true,
23274        /**
23275         * @event beforemove
23276         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23277         * @param {Tree} tree The owner tree
23278         * @param {Node} this This node
23279         * @param {Node} oldParent The parent of this node
23280         * @param {Node} newParent The new parent this node is moving to
23281         * @param {Number} index The index it is being moved to
23282         */
23283        "beforemove" : true,
23284        /**
23285         * @event beforeinsert
23286         * Fires before a new child is inserted, return false to cancel the insert.
23287         * @param {Tree} tree The owner tree
23288         * @param {Node} this This node
23289         * @param {Node} node The child node to be inserted
23290         * @param {Node} refNode The child node the node is being inserted before
23291         */
23292        "beforeinsert" : true
23293    });
23294     this.listeners = this.attributes.listeners;
23295     Roo.data.Node.superclass.constructor.call(this);
23296 };
23297
23298 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23299     fireEvent : function(evtName){
23300         // first do standard event for this node
23301         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23302             return false;
23303         }
23304         // then bubble it up to the tree if the event wasn't cancelled
23305         var ot = this.getOwnerTree();
23306         if(ot){
23307             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23308                 return false;
23309             }
23310         }
23311         return true;
23312     },
23313
23314     /**
23315      * Returns true if this node is a leaf
23316      * @return {Boolean}
23317      */
23318     isLeaf : function(){
23319         return this.leaf === true;
23320     },
23321
23322     // private
23323     setFirstChild : function(node){
23324         this.firstChild = node;
23325     },
23326
23327     //private
23328     setLastChild : function(node){
23329         this.lastChild = node;
23330     },
23331
23332
23333     /**
23334      * Returns true if this node is the last child of its parent
23335      * @return {Boolean}
23336      */
23337     isLast : function(){
23338        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23339     },
23340
23341     /**
23342      * Returns true if this node is the first child of its parent
23343      * @return {Boolean}
23344      */
23345     isFirst : function(){
23346        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23347     },
23348
23349     hasChildNodes : function(){
23350         return !this.isLeaf() && this.childNodes.length > 0;
23351     },
23352
23353     /**
23354      * Insert node(s) as the last child node of this node.
23355      * @param {Node/Array} node The node or Array of nodes to append
23356      * @return {Node} The appended node if single append, or null if an array was passed
23357      */
23358     appendChild : function(node){
23359         var multi = false;
23360         if(node instanceof Array){
23361             multi = node;
23362         }else if(arguments.length > 1){
23363             multi = arguments;
23364         }
23365         // if passed an array or multiple args do them one by one
23366         if(multi){
23367             for(var i = 0, len = multi.length; i < len; i++) {
23368                 this.appendChild(multi[i]);
23369             }
23370         }else{
23371             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23372                 return false;
23373             }
23374             var index = this.childNodes.length;
23375             var oldParent = node.parentNode;
23376             // it's a move, make sure we move it cleanly
23377             if(oldParent){
23378                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23379                     return false;
23380                 }
23381                 oldParent.removeChild(node);
23382             }
23383             index = this.childNodes.length;
23384             if(index == 0){
23385                 this.setFirstChild(node);
23386             }
23387             this.childNodes.push(node);
23388             node.parentNode = this;
23389             var ps = this.childNodes[index-1];
23390             if(ps){
23391                 node.previousSibling = ps;
23392                 ps.nextSibling = node;
23393             }else{
23394                 node.previousSibling = null;
23395             }
23396             node.nextSibling = null;
23397             this.setLastChild(node);
23398             node.setOwnerTree(this.getOwnerTree());
23399             this.fireEvent("append", this.ownerTree, this, node, index);
23400             if(oldParent){
23401                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23402             }
23403             return node;
23404         }
23405     },
23406
23407     /**
23408      * Removes a child node from this node.
23409      * @param {Node} node The node to remove
23410      * @return {Node} The removed node
23411      */
23412     removeChild : function(node){
23413         var index = this.childNodes.indexOf(node);
23414         if(index == -1){
23415             return false;
23416         }
23417         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23418             return false;
23419         }
23420
23421         // remove it from childNodes collection
23422         this.childNodes.splice(index, 1);
23423
23424         // update siblings
23425         if(node.previousSibling){
23426             node.previousSibling.nextSibling = node.nextSibling;
23427         }
23428         if(node.nextSibling){
23429             node.nextSibling.previousSibling = node.previousSibling;
23430         }
23431
23432         // update child refs
23433         if(this.firstChild == node){
23434             this.setFirstChild(node.nextSibling);
23435         }
23436         if(this.lastChild == node){
23437             this.setLastChild(node.previousSibling);
23438         }
23439
23440         node.setOwnerTree(null);
23441         // clear any references from the node
23442         node.parentNode = null;
23443         node.previousSibling = null;
23444         node.nextSibling = null;
23445         this.fireEvent("remove", this.ownerTree, this, node);
23446         return node;
23447     },
23448
23449     /**
23450      * Inserts the first node before the second node in this nodes childNodes collection.
23451      * @param {Node} node The node to insert
23452      * @param {Node} refNode The node to insert before (if null the node is appended)
23453      * @return {Node} The inserted node
23454      */
23455     insertBefore : function(node, refNode){
23456         if(!refNode){ // like standard Dom, refNode can be null for append
23457             return this.appendChild(node);
23458         }
23459         // nothing to do
23460         if(node == refNode){
23461             return false;
23462         }
23463
23464         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23465             return false;
23466         }
23467         var index = this.childNodes.indexOf(refNode);
23468         var oldParent = node.parentNode;
23469         var refIndex = index;
23470
23471         // when moving internally, indexes will change after remove
23472         if(oldParent == this && this.childNodes.indexOf(node) < index){
23473             refIndex--;
23474         }
23475
23476         // it's a move, make sure we move it cleanly
23477         if(oldParent){
23478             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23479                 return false;
23480             }
23481             oldParent.removeChild(node);
23482         }
23483         if(refIndex == 0){
23484             this.setFirstChild(node);
23485         }
23486         this.childNodes.splice(refIndex, 0, node);
23487         node.parentNode = this;
23488         var ps = this.childNodes[refIndex-1];
23489         if(ps){
23490             node.previousSibling = ps;
23491             ps.nextSibling = node;
23492         }else{
23493             node.previousSibling = null;
23494         }
23495         node.nextSibling = refNode;
23496         refNode.previousSibling = node;
23497         node.setOwnerTree(this.getOwnerTree());
23498         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23499         if(oldParent){
23500             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23501         }
23502         return node;
23503     },
23504
23505     /**
23506      * Returns the child node at the specified index.
23507      * @param {Number} index
23508      * @return {Node}
23509      */
23510     item : function(index){
23511         return this.childNodes[index];
23512     },
23513
23514     /**
23515      * Replaces one child node in this node with another.
23516      * @param {Node} newChild The replacement node
23517      * @param {Node} oldChild The node to replace
23518      * @return {Node} The replaced node
23519      */
23520     replaceChild : function(newChild, oldChild){
23521         this.insertBefore(newChild, oldChild);
23522         this.removeChild(oldChild);
23523         return oldChild;
23524     },
23525
23526     /**
23527      * Returns the index of a child node
23528      * @param {Node} node
23529      * @return {Number} The index of the node or -1 if it was not found
23530      */
23531     indexOf : function(child){
23532         return this.childNodes.indexOf(child);
23533     },
23534
23535     /**
23536      * Returns the tree this node is in.
23537      * @return {Tree}
23538      */
23539     getOwnerTree : function(){
23540         // if it doesn't have one, look for one
23541         if(!this.ownerTree){
23542             var p = this;
23543             while(p){
23544                 if(p.ownerTree){
23545                     this.ownerTree = p.ownerTree;
23546                     break;
23547                 }
23548                 p = p.parentNode;
23549             }
23550         }
23551         return this.ownerTree;
23552     },
23553
23554     /**
23555      * Returns depth of this node (the root node has a depth of 0)
23556      * @return {Number}
23557      */
23558     getDepth : function(){
23559         var depth = 0;
23560         var p = this;
23561         while(p.parentNode){
23562             ++depth;
23563             p = p.parentNode;
23564         }
23565         return depth;
23566     },
23567
23568     // private
23569     setOwnerTree : function(tree){
23570         // if it's move, we need to update everyone
23571         if(tree != this.ownerTree){
23572             if(this.ownerTree){
23573                 this.ownerTree.unregisterNode(this);
23574             }
23575             this.ownerTree = tree;
23576             var cs = this.childNodes;
23577             for(var i = 0, len = cs.length; i < len; i++) {
23578                 cs[i].setOwnerTree(tree);
23579             }
23580             if(tree){
23581                 tree.registerNode(this);
23582             }
23583         }
23584     },
23585
23586     /**
23587      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23588      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23589      * @return {String} The path
23590      */
23591     getPath : function(attr){
23592         attr = attr || "id";
23593         var p = this.parentNode;
23594         var b = [this.attributes[attr]];
23595         while(p){
23596             b.unshift(p.attributes[attr]);
23597             p = p.parentNode;
23598         }
23599         var sep = this.getOwnerTree().pathSeparator;
23600         return sep + b.join(sep);
23601     },
23602
23603     /**
23604      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23605      * function call will be the scope provided or the current node. The arguments to the function
23606      * will be the args provided or the current node. If the function returns false at any point,
23607      * the bubble is stopped.
23608      * @param {Function} fn The function to call
23609      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23610      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23611      */
23612     bubble : function(fn, scope, args){
23613         var p = this;
23614         while(p){
23615             if(fn.call(scope || p, args || p) === false){
23616                 break;
23617             }
23618             p = p.parentNode;
23619         }
23620     },
23621
23622     /**
23623      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23624      * function call will be the scope provided or the current node. The arguments to the function
23625      * will be the args provided or the current node. If the function returns false at any point,
23626      * the cascade is stopped on that branch.
23627      * @param {Function} fn The function to call
23628      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23629      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23630      */
23631     cascade : function(fn, scope, args){
23632         if(fn.call(scope || this, args || this) !== false){
23633             var cs = this.childNodes;
23634             for(var i = 0, len = cs.length; i < len; i++) {
23635                 cs[i].cascade(fn, scope, args);
23636             }
23637         }
23638     },
23639
23640     /**
23641      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23642      * function call will be the scope provided or the current node. The arguments to the function
23643      * will be the args provided or the current node. If the function returns false at any point,
23644      * the iteration stops.
23645      * @param {Function} fn The function to call
23646      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23647      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23648      */
23649     eachChild : function(fn, scope, args){
23650         var cs = this.childNodes;
23651         for(var i = 0, len = cs.length; i < len; i++) {
23652                 if(fn.call(scope || this, args || cs[i]) === false){
23653                     break;
23654                 }
23655         }
23656     },
23657
23658     /**
23659      * Finds the first child that has the attribute with the specified value.
23660      * @param {String} attribute The attribute name
23661      * @param {Mixed} value The value to search for
23662      * @return {Node} The found child or null if none was found
23663      */
23664     findChild : function(attribute, value){
23665         var cs = this.childNodes;
23666         for(var i = 0, len = cs.length; i < len; i++) {
23667                 if(cs[i].attributes[attribute] == value){
23668                     return cs[i];
23669                 }
23670         }
23671         return null;
23672     },
23673
23674     /**
23675      * Finds the first child by a custom function. The child matches if the function passed
23676      * returns true.
23677      * @param {Function} fn
23678      * @param {Object} scope (optional)
23679      * @return {Node} The found child or null if none was found
23680      */
23681     findChildBy : function(fn, scope){
23682         var cs = this.childNodes;
23683         for(var i = 0, len = cs.length; i < len; i++) {
23684                 if(fn.call(scope||cs[i], cs[i]) === true){
23685                     return cs[i];
23686                 }
23687         }
23688         return null;
23689     },
23690
23691     /**
23692      * Sorts this nodes children using the supplied sort function
23693      * @param {Function} fn
23694      * @param {Object} scope (optional)
23695      */
23696     sort : function(fn, scope){
23697         var cs = this.childNodes;
23698         var len = cs.length;
23699         if(len > 0){
23700             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23701             cs.sort(sortFn);
23702             for(var i = 0; i < len; i++){
23703                 var n = cs[i];
23704                 n.previousSibling = cs[i-1];
23705                 n.nextSibling = cs[i+1];
23706                 if(i == 0){
23707                     this.setFirstChild(n);
23708                 }
23709                 if(i == len-1){
23710                     this.setLastChild(n);
23711                 }
23712             }
23713         }
23714     },
23715
23716     /**
23717      * Returns true if this node is an ancestor (at any point) of the passed node.
23718      * @param {Node} node
23719      * @return {Boolean}
23720      */
23721     contains : function(node){
23722         return node.isAncestor(this);
23723     },
23724
23725     /**
23726      * Returns true if the passed node is an ancestor (at any point) of this node.
23727      * @param {Node} node
23728      * @return {Boolean}
23729      */
23730     isAncestor : function(node){
23731         var p = this.parentNode;
23732         while(p){
23733             if(p == node){
23734                 return true;
23735             }
23736             p = p.parentNode;
23737         }
23738         return false;
23739     },
23740
23741     toString : function(){
23742         return "[Node"+(this.id?" "+this.id:"")+"]";
23743     }
23744 });/*
23745  * Based on:
23746  * Ext JS Library 1.1.1
23747  * Copyright(c) 2006-2007, Ext JS, LLC.
23748  *
23749  * Originally Released Under LGPL - original licence link has changed is not relivant.
23750  *
23751  * Fork - LGPL
23752  * <script type="text/javascript">
23753  */
23754  (function(){ 
23755 /**
23756  * @class Roo.Layer
23757  * @extends Roo.Element
23758  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23759  * automatic maintaining of shadow/shim positions.
23760  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23761  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23762  * you can pass a string with a CSS class name. False turns off the shadow.
23763  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23764  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23765  * @cfg {String} cls CSS class to add to the element
23766  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23767  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23768  * @constructor
23769  * @param {Object} config An object with config options.
23770  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23771  */
23772
23773 Roo.Layer = function(config, existingEl){
23774     config = config || {};
23775     var dh = Roo.DomHelper;
23776     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23777     if(existingEl){
23778         this.dom = Roo.getDom(existingEl);
23779     }
23780     if(!this.dom){
23781         var o = config.dh || {tag: "div", cls: "x-layer"};
23782         this.dom = dh.append(pel, o);
23783     }
23784     if(config.cls){
23785         this.addClass(config.cls);
23786     }
23787     this.constrain = config.constrain !== false;
23788     this.visibilityMode = Roo.Element.VISIBILITY;
23789     if(config.id){
23790         this.id = this.dom.id = config.id;
23791     }else{
23792         this.id = Roo.id(this.dom);
23793     }
23794     this.zindex = config.zindex || this.getZIndex();
23795     this.position("absolute", this.zindex);
23796     if(config.shadow){
23797         this.shadowOffset = config.shadowOffset || 4;
23798         this.shadow = new Roo.Shadow({
23799             offset : this.shadowOffset,
23800             mode : config.shadow
23801         });
23802     }else{
23803         this.shadowOffset = 0;
23804     }
23805     this.useShim = config.shim !== false && Roo.useShims;
23806     this.useDisplay = config.useDisplay;
23807     this.hide();
23808 };
23809
23810 var supr = Roo.Element.prototype;
23811
23812 // shims are shared among layer to keep from having 100 iframes
23813 var shims = [];
23814
23815 Roo.extend(Roo.Layer, Roo.Element, {
23816
23817     getZIndex : function(){
23818         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23819     },
23820
23821     getShim : function(){
23822         if(!this.useShim){
23823             return null;
23824         }
23825         if(this.shim){
23826             return this.shim;
23827         }
23828         var shim = shims.shift();
23829         if(!shim){
23830             shim = this.createShim();
23831             shim.enableDisplayMode('block');
23832             shim.dom.style.display = 'none';
23833             shim.dom.style.visibility = 'visible';
23834         }
23835         var pn = this.dom.parentNode;
23836         if(shim.dom.parentNode != pn){
23837             pn.insertBefore(shim.dom, this.dom);
23838         }
23839         shim.setStyle('z-index', this.getZIndex()-2);
23840         this.shim = shim;
23841         return shim;
23842     },
23843
23844     hideShim : function(){
23845         if(this.shim){
23846             this.shim.setDisplayed(false);
23847             shims.push(this.shim);
23848             delete this.shim;
23849         }
23850     },
23851
23852     disableShadow : function(){
23853         if(this.shadow){
23854             this.shadowDisabled = true;
23855             this.shadow.hide();
23856             this.lastShadowOffset = this.shadowOffset;
23857             this.shadowOffset = 0;
23858         }
23859     },
23860
23861     enableShadow : function(show){
23862         if(this.shadow){
23863             this.shadowDisabled = false;
23864             this.shadowOffset = this.lastShadowOffset;
23865             delete this.lastShadowOffset;
23866             if(show){
23867                 this.sync(true);
23868             }
23869         }
23870     },
23871
23872     // private
23873     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23874     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23875     sync : function(doShow){
23876         var sw = this.shadow;
23877         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23878             var sh = this.getShim();
23879
23880             var w = this.getWidth(),
23881                 h = this.getHeight();
23882
23883             var l = this.getLeft(true),
23884                 t = this.getTop(true);
23885
23886             if(sw && !this.shadowDisabled){
23887                 if(doShow && !sw.isVisible()){
23888                     sw.show(this);
23889                 }else{
23890                     sw.realign(l, t, w, h);
23891                 }
23892                 if(sh){
23893                     if(doShow){
23894                        sh.show();
23895                     }
23896                     // fit the shim behind the shadow, so it is shimmed too
23897                     var a = sw.adjusts, s = sh.dom.style;
23898                     s.left = (Math.min(l, l+a.l))+"px";
23899                     s.top = (Math.min(t, t+a.t))+"px";
23900                     s.width = (w+a.w)+"px";
23901                     s.height = (h+a.h)+"px";
23902                 }
23903             }else if(sh){
23904                 if(doShow){
23905                    sh.show();
23906                 }
23907                 sh.setSize(w, h);
23908                 sh.setLeftTop(l, t);
23909             }
23910             
23911         }
23912     },
23913
23914     // private
23915     destroy : function(){
23916         this.hideShim();
23917         if(this.shadow){
23918             this.shadow.hide();
23919         }
23920         this.removeAllListeners();
23921         var pn = this.dom.parentNode;
23922         if(pn){
23923             pn.removeChild(this.dom);
23924         }
23925         Roo.Element.uncache(this.id);
23926     },
23927
23928     remove : function(){
23929         this.destroy();
23930     },
23931
23932     // private
23933     beginUpdate : function(){
23934         this.updating = true;
23935     },
23936
23937     // private
23938     endUpdate : function(){
23939         this.updating = false;
23940         this.sync(true);
23941     },
23942
23943     // private
23944     hideUnders : function(negOffset){
23945         if(this.shadow){
23946             this.shadow.hide();
23947         }
23948         this.hideShim();
23949     },
23950
23951     // private
23952     constrainXY : function(){
23953         if(this.constrain){
23954             var vw = Roo.lib.Dom.getViewWidth(),
23955                 vh = Roo.lib.Dom.getViewHeight();
23956             var s = Roo.get(document).getScroll();
23957
23958             var xy = this.getXY();
23959             var x = xy[0], y = xy[1];   
23960             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23961             // only move it if it needs it
23962             var moved = false;
23963             // first validate right/bottom
23964             if((x + w) > vw+s.left){
23965                 x = vw - w - this.shadowOffset;
23966                 moved = true;
23967             }
23968             if((y + h) > vh+s.top){
23969                 y = vh - h - this.shadowOffset;
23970                 moved = true;
23971             }
23972             // then make sure top/left isn't negative
23973             if(x < s.left){
23974                 x = s.left;
23975                 moved = true;
23976             }
23977             if(y < s.top){
23978                 y = s.top;
23979                 moved = true;
23980             }
23981             if(moved){
23982                 if(this.avoidY){
23983                     var ay = this.avoidY;
23984                     if(y <= ay && (y+h) >= ay){
23985                         y = ay-h-5;   
23986                     }
23987                 }
23988                 xy = [x, y];
23989                 this.storeXY(xy);
23990                 supr.setXY.call(this, xy);
23991                 this.sync();
23992             }
23993         }
23994     },
23995
23996     isVisible : function(){
23997         return this.visible;    
23998     },
23999
24000     // private
24001     showAction : function(){
24002         this.visible = true; // track visibility to prevent getStyle calls
24003         if(this.useDisplay === true){
24004             this.setDisplayed("");
24005         }else if(this.lastXY){
24006             supr.setXY.call(this, this.lastXY);
24007         }else if(this.lastLT){
24008             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
24009         }
24010     },
24011
24012     // private
24013     hideAction : function(){
24014         this.visible = false;
24015         if(this.useDisplay === true){
24016             this.setDisplayed(false);
24017         }else{
24018             this.setLeftTop(-10000,-10000);
24019         }
24020     },
24021
24022     // overridden Element method
24023     setVisible : function(v, a, d, c, e){
24024         if(v){
24025             this.showAction();
24026         }
24027         if(a && v){
24028             var cb = function(){
24029                 this.sync(true);
24030                 if(c){
24031                     c();
24032                 }
24033             }.createDelegate(this);
24034             supr.setVisible.call(this, true, true, d, cb, e);
24035         }else{
24036             if(!v){
24037                 this.hideUnders(true);
24038             }
24039             var cb = c;
24040             if(a){
24041                 cb = function(){
24042                     this.hideAction();
24043                     if(c){
24044                         c();
24045                     }
24046                 }.createDelegate(this);
24047             }
24048             supr.setVisible.call(this, v, a, d, cb, e);
24049             if(v){
24050                 this.sync(true);
24051             }else if(!a){
24052                 this.hideAction();
24053             }
24054         }
24055     },
24056
24057     storeXY : function(xy){
24058         delete this.lastLT;
24059         this.lastXY = xy;
24060     },
24061
24062     storeLeftTop : function(left, top){
24063         delete this.lastXY;
24064         this.lastLT = [left, top];
24065     },
24066
24067     // private
24068     beforeFx : function(){
24069         this.beforeAction();
24070         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24071     },
24072
24073     // private
24074     afterFx : function(){
24075         Roo.Layer.superclass.afterFx.apply(this, arguments);
24076         this.sync(this.isVisible());
24077     },
24078
24079     // private
24080     beforeAction : function(){
24081         if(!this.updating && this.shadow){
24082             this.shadow.hide();
24083         }
24084     },
24085
24086     // overridden Element method
24087     setLeft : function(left){
24088         this.storeLeftTop(left, this.getTop(true));
24089         supr.setLeft.apply(this, arguments);
24090         this.sync();
24091     },
24092
24093     setTop : function(top){
24094         this.storeLeftTop(this.getLeft(true), top);
24095         supr.setTop.apply(this, arguments);
24096         this.sync();
24097     },
24098
24099     setLeftTop : function(left, top){
24100         this.storeLeftTop(left, top);
24101         supr.setLeftTop.apply(this, arguments);
24102         this.sync();
24103     },
24104
24105     setXY : function(xy, a, d, c, e){
24106         this.fixDisplay();
24107         this.beforeAction();
24108         this.storeXY(xy);
24109         var cb = this.createCB(c);
24110         supr.setXY.call(this, xy, a, d, cb, e);
24111         if(!a){
24112             cb();
24113         }
24114     },
24115
24116     // private
24117     createCB : function(c){
24118         var el = this;
24119         return function(){
24120             el.constrainXY();
24121             el.sync(true);
24122             if(c){
24123                 c();
24124             }
24125         };
24126     },
24127
24128     // overridden Element method
24129     setX : function(x, a, d, c, e){
24130         this.setXY([x, this.getY()], a, d, c, e);
24131     },
24132
24133     // overridden Element method
24134     setY : function(y, a, d, c, e){
24135         this.setXY([this.getX(), y], a, d, c, e);
24136     },
24137
24138     // overridden Element method
24139     setSize : function(w, h, a, d, c, e){
24140         this.beforeAction();
24141         var cb = this.createCB(c);
24142         supr.setSize.call(this, w, h, a, d, cb, e);
24143         if(!a){
24144             cb();
24145         }
24146     },
24147
24148     // overridden Element method
24149     setWidth : function(w, a, d, c, e){
24150         this.beforeAction();
24151         var cb = this.createCB(c);
24152         supr.setWidth.call(this, w, a, d, cb, e);
24153         if(!a){
24154             cb();
24155         }
24156     },
24157
24158     // overridden Element method
24159     setHeight : function(h, a, d, c, e){
24160         this.beforeAction();
24161         var cb = this.createCB(c);
24162         supr.setHeight.call(this, h, a, d, cb, e);
24163         if(!a){
24164             cb();
24165         }
24166     },
24167
24168     // overridden Element method
24169     setBounds : function(x, y, w, h, a, d, c, e){
24170         this.beforeAction();
24171         var cb = this.createCB(c);
24172         if(!a){
24173             this.storeXY([x, y]);
24174             supr.setXY.call(this, [x, y]);
24175             supr.setSize.call(this, w, h, a, d, cb, e);
24176             cb();
24177         }else{
24178             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24179         }
24180         return this;
24181     },
24182     
24183     /**
24184      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24185      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24186      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24187      * @param {Number} zindex The new z-index to set
24188      * @return {this} The Layer
24189      */
24190     setZIndex : function(zindex){
24191         this.zindex = zindex;
24192         this.setStyle("z-index", zindex + 2);
24193         if(this.shadow){
24194             this.shadow.setZIndex(zindex + 1);
24195         }
24196         if(this.shim){
24197             this.shim.setStyle("z-index", zindex);
24198         }
24199     }
24200 });
24201 })();/*
24202  * Based on:
24203  * Ext JS Library 1.1.1
24204  * Copyright(c) 2006-2007, Ext JS, LLC.
24205  *
24206  * Originally Released Under LGPL - original licence link has changed is not relivant.
24207  *
24208  * Fork - LGPL
24209  * <script type="text/javascript">
24210  */
24211
24212
24213 /**
24214  * @class Roo.Shadow
24215  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24216  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24217  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24218  * @constructor
24219  * Create a new Shadow
24220  * @param {Object} config The config object
24221  */
24222 Roo.Shadow = function(config){
24223     Roo.apply(this, config);
24224     if(typeof this.mode != "string"){
24225         this.mode = this.defaultMode;
24226     }
24227     var o = this.offset, a = {h: 0};
24228     var rad = Math.floor(this.offset/2);
24229     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24230         case "drop":
24231             a.w = 0;
24232             a.l = a.t = o;
24233             a.t -= 1;
24234             if(Roo.isIE){
24235                 a.l -= this.offset + rad;
24236                 a.t -= this.offset + rad;
24237                 a.w -= rad;
24238                 a.h -= rad;
24239                 a.t += 1;
24240             }
24241         break;
24242         case "sides":
24243             a.w = (o*2);
24244             a.l = -o;
24245             a.t = o-1;
24246             if(Roo.isIE){
24247                 a.l -= (this.offset - rad);
24248                 a.t -= this.offset + rad;
24249                 a.l += 1;
24250                 a.w -= (this.offset - rad)*2;
24251                 a.w -= rad + 1;
24252                 a.h -= 1;
24253             }
24254         break;
24255         case "frame":
24256             a.w = a.h = (o*2);
24257             a.l = a.t = -o;
24258             a.t += 1;
24259             a.h -= 2;
24260             if(Roo.isIE){
24261                 a.l -= (this.offset - rad);
24262                 a.t -= (this.offset - rad);
24263                 a.l += 1;
24264                 a.w -= (this.offset + rad + 1);
24265                 a.h -= (this.offset + rad);
24266                 a.h += 1;
24267             }
24268         break;
24269     };
24270
24271     this.adjusts = a;
24272 };
24273
24274 Roo.Shadow.prototype = {
24275     /**
24276      * @cfg {String} mode
24277      * The shadow display mode.  Supports the following options:<br />
24278      * sides: Shadow displays on both sides and bottom only<br />
24279      * frame: Shadow displays equally on all four sides<br />
24280      * drop: Traditional bottom-right drop shadow (default)
24281      */
24282     /**
24283      * @cfg {String} offset
24284      * The number of pixels to offset the shadow from the element (defaults to 4)
24285      */
24286     offset: 4,
24287
24288     // private
24289     defaultMode: "drop",
24290
24291     /**
24292      * Displays the shadow under the target element
24293      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24294      */
24295     show : function(target){
24296         target = Roo.get(target);
24297         if(!this.el){
24298             this.el = Roo.Shadow.Pool.pull();
24299             if(this.el.dom.nextSibling != target.dom){
24300                 this.el.insertBefore(target);
24301             }
24302         }
24303         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24304         if(Roo.isIE){
24305             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24306         }
24307         this.realign(
24308             target.getLeft(true),
24309             target.getTop(true),
24310             target.getWidth(),
24311             target.getHeight()
24312         );
24313         this.el.dom.style.display = "block";
24314     },
24315
24316     /**
24317      * Returns true if the shadow is visible, else false
24318      */
24319     isVisible : function(){
24320         return this.el ? true : false;  
24321     },
24322
24323     /**
24324      * Direct alignment when values are already available. Show must be called at least once before
24325      * calling this method to ensure it is initialized.
24326      * @param {Number} left The target element left position
24327      * @param {Number} top The target element top position
24328      * @param {Number} width The target element width
24329      * @param {Number} height The target element height
24330      */
24331     realign : function(l, t, w, h){
24332         if(!this.el){
24333             return;
24334         }
24335         var a = this.adjusts, d = this.el.dom, s = d.style;
24336         var iea = 0;
24337         s.left = (l+a.l)+"px";
24338         s.top = (t+a.t)+"px";
24339         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24340  
24341         if(s.width != sws || s.height != shs){
24342             s.width = sws;
24343             s.height = shs;
24344             if(!Roo.isIE){
24345                 var cn = d.childNodes;
24346                 var sww = Math.max(0, (sw-12))+"px";
24347                 cn[0].childNodes[1].style.width = sww;
24348                 cn[1].childNodes[1].style.width = sww;
24349                 cn[2].childNodes[1].style.width = sww;
24350                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24351             }
24352         }
24353     },
24354
24355     /**
24356      * Hides this shadow
24357      */
24358     hide : function(){
24359         if(this.el){
24360             this.el.dom.style.display = "none";
24361             Roo.Shadow.Pool.push(this.el);
24362             delete this.el;
24363         }
24364     },
24365
24366     /**
24367      * Adjust the z-index of this shadow
24368      * @param {Number} zindex The new z-index
24369      */
24370     setZIndex : function(z){
24371         this.zIndex = z;
24372         if(this.el){
24373             this.el.setStyle("z-index", z);
24374         }
24375     }
24376 };
24377
24378 // Private utility class that manages the internal Shadow cache
24379 Roo.Shadow.Pool = function(){
24380     var p = [];
24381     var markup = Roo.isIE ?
24382                  '<div class="x-ie-shadow"></div>' :
24383                  '<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>';
24384     return {
24385         pull : function(){
24386             var sh = p.shift();
24387             if(!sh){
24388                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24389                 sh.autoBoxAdjust = false;
24390             }
24391             return sh;
24392         },
24393
24394         push : function(sh){
24395             p.push(sh);
24396         }
24397     };
24398 }();/*
24399  * Based on:
24400  * Ext JS Library 1.1.1
24401  * Copyright(c) 2006-2007, Ext JS, LLC.
24402  *
24403  * Originally Released Under LGPL - original licence link has changed is not relivant.
24404  *
24405  * Fork - LGPL
24406  * <script type="text/javascript">
24407  */
24408
24409
24410 /**
24411  * @class Roo.SplitBar
24412  * @extends Roo.util.Observable
24413  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24414  * <br><br>
24415  * Usage:
24416  * <pre><code>
24417 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24418                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24419 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24420 split.minSize = 100;
24421 split.maxSize = 600;
24422 split.animate = true;
24423 split.on('moved', splitterMoved);
24424 </code></pre>
24425  * @constructor
24426  * Create a new SplitBar
24427  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24428  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24429  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24430  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24431                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24432                         position of the SplitBar).
24433  */
24434 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24435     
24436     /** @private */
24437     this.el = Roo.get(dragElement, true);
24438     this.el.dom.unselectable = "on";
24439     /** @private */
24440     this.resizingEl = Roo.get(resizingElement, true);
24441
24442     /**
24443      * @private
24444      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24445      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24446      * @type Number
24447      */
24448     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24449     
24450     /**
24451      * The minimum size of the resizing element. (Defaults to 0)
24452      * @type Number
24453      */
24454     this.minSize = 0;
24455     
24456     /**
24457      * The maximum size of the resizing element. (Defaults to 2000)
24458      * @type Number
24459      */
24460     this.maxSize = 2000;
24461     
24462     /**
24463      * Whether to animate the transition to the new size
24464      * @type Boolean
24465      */
24466     this.animate = false;
24467     
24468     /**
24469      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24470      * @type Boolean
24471      */
24472     this.useShim = false;
24473     
24474     /** @private */
24475     this.shim = null;
24476     
24477     if(!existingProxy){
24478         /** @private */
24479         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24480     }else{
24481         this.proxy = Roo.get(existingProxy).dom;
24482     }
24483     /** @private */
24484     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24485     
24486     /** @private */
24487     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24488     
24489     /** @private */
24490     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24491     
24492     /** @private */
24493     this.dragSpecs = {};
24494     
24495     /**
24496      * @private The adapter to use to positon and resize elements
24497      */
24498     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24499     this.adapter.init(this);
24500     
24501     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24502         /** @private */
24503         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24504         this.el.addClass("x-splitbar-h");
24505     }else{
24506         /** @private */
24507         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24508         this.el.addClass("x-splitbar-v");
24509     }
24510     
24511     this.addEvents({
24512         /**
24513          * @event resize
24514          * Fires when the splitter is moved (alias for {@link #event-moved})
24515          * @param {Roo.SplitBar} this
24516          * @param {Number} newSize the new width or height
24517          */
24518         "resize" : true,
24519         /**
24520          * @event moved
24521          * Fires when the splitter is moved
24522          * @param {Roo.SplitBar} this
24523          * @param {Number} newSize the new width or height
24524          */
24525         "moved" : true,
24526         /**
24527          * @event beforeresize
24528          * Fires before the splitter is dragged
24529          * @param {Roo.SplitBar} this
24530          */
24531         "beforeresize" : true,
24532
24533         "beforeapply" : true
24534     });
24535
24536     Roo.util.Observable.call(this);
24537 };
24538
24539 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24540     onStartProxyDrag : function(x, y){
24541         this.fireEvent("beforeresize", this);
24542         if(!this.overlay){
24543             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24544             o.unselectable();
24545             o.enableDisplayMode("block");
24546             // all splitbars share the same overlay
24547             Roo.SplitBar.prototype.overlay = o;
24548         }
24549         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24550         this.overlay.show();
24551         Roo.get(this.proxy).setDisplayed("block");
24552         var size = this.adapter.getElementSize(this);
24553         this.activeMinSize = this.getMinimumSize();;
24554         this.activeMaxSize = this.getMaximumSize();;
24555         var c1 = size - this.activeMinSize;
24556         var c2 = Math.max(this.activeMaxSize - size, 0);
24557         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24558             this.dd.resetConstraints();
24559             this.dd.setXConstraint(
24560                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24561                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24562             );
24563             this.dd.setYConstraint(0, 0);
24564         }else{
24565             this.dd.resetConstraints();
24566             this.dd.setXConstraint(0, 0);
24567             this.dd.setYConstraint(
24568                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24569                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24570             );
24571          }
24572         this.dragSpecs.startSize = size;
24573         this.dragSpecs.startPoint = [x, y];
24574         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24575     },
24576     
24577     /** 
24578      * @private Called after the drag operation by the DDProxy
24579      */
24580     onEndProxyDrag : function(e){
24581         Roo.get(this.proxy).setDisplayed(false);
24582         var endPoint = Roo.lib.Event.getXY(e);
24583         if(this.overlay){
24584             this.overlay.hide();
24585         }
24586         var newSize;
24587         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24588             newSize = this.dragSpecs.startSize + 
24589                 (this.placement == Roo.SplitBar.LEFT ?
24590                     endPoint[0] - this.dragSpecs.startPoint[0] :
24591                     this.dragSpecs.startPoint[0] - endPoint[0]
24592                 );
24593         }else{
24594             newSize = this.dragSpecs.startSize + 
24595                 (this.placement == Roo.SplitBar.TOP ?
24596                     endPoint[1] - this.dragSpecs.startPoint[1] :
24597                     this.dragSpecs.startPoint[1] - endPoint[1]
24598                 );
24599         }
24600         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24601         if(newSize != this.dragSpecs.startSize){
24602             if(this.fireEvent('beforeapply', this, newSize) !== false){
24603                 this.adapter.setElementSize(this, newSize);
24604                 this.fireEvent("moved", this, newSize);
24605                 this.fireEvent("resize", this, newSize);
24606             }
24607         }
24608     },
24609     
24610     /**
24611      * Get the adapter this SplitBar uses
24612      * @return The adapter object
24613      */
24614     getAdapter : function(){
24615         return this.adapter;
24616     },
24617     
24618     /**
24619      * Set the adapter this SplitBar uses
24620      * @param {Object} adapter A SplitBar adapter object
24621      */
24622     setAdapter : function(adapter){
24623         this.adapter = adapter;
24624         this.adapter.init(this);
24625     },
24626     
24627     /**
24628      * Gets the minimum size for the resizing element
24629      * @return {Number} The minimum size
24630      */
24631     getMinimumSize : function(){
24632         return this.minSize;
24633     },
24634     
24635     /**
24636      * Sets the minimum size for the resizing element
24637      * @param {Number} minSize The minimum size
24638      */
24639     setMinimumSize : function(minSize){
24640         this.minSize = minSize;
24641     },
24642     
24643     /**
24644      * Gets the maximum size for the resizing element
24645      * @return {Number} The maximum size
24646      */
24647     getMaximumSize : function(){
24648         return this.maxSize;
24649     },
24650     
24651     /**
24652      * Sets the maximum size for the resizing element
24653      * @param {Number} maxSize The maximum size
24654      */
24655     setMaximumSize : function(maxSize){
24656         this.maxSize = maxSize;
24657     },
24658     
24659     /**
24660      * Sets the initialize size for the resizing element
24661      * @param {Number} size The initial size
24662      */
24663     setCurrentSize : function(size){
24664         var oldAnimate = this.animate;
24665         this.animate = false;
24666         this.adapter.setElementSize(this, size);
24667         this.animate = oldAnimate;
24668     },
24669     
24670     /**
24671      * Destroy this splitbar. 
24672      * @param {Boolean} removeEl True to remove the element
24673      */
24674     destroy : function(removeEl){
24675         if(this.shim){
24676             this.shim.remove();
24677         }
24678         this.dd.unreg();
24679         this.proxy.parentNode.removeChild(this.proxy);
24680         if(removeEl){
24681             this.el.remove();
24682         }
24683     }
24684 });
24685
24686 /**
24687  * @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.
24688  */
24689 Roo.SplitBar.createProxy = function(dir){
24690     var proxy = new Roo.Element(document.createElement("div"));
24691     proxy.unselectable();
24692     var cls = 'x-splitbar-proxy';
24693     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24694     document.body.appendChild(proxy.dom);
24695     return proxy.dom;
24696 };
24697
24698 /** 
24699  * @class Roo.SplitBar.BasicLayoutAdapter
24700  * Default Adapter. It assumes the splitter and resizing element are not positioned
24701  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24702  */
24703 Roo.SplitBar.BasicLayoutAdapter = function(){
24704 };
24705
24706 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24707     // do nothing for now
24708     init : function(s){
24709     
24710     },
24711     /**
24712      * Called before drag operations to get the current size of the resizing element. 
24713      * @param {Roo.SplitBar} s The SplitBar using this adapter
24714      */
24715      getElementSize : function(s){
24716         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24717             return s.resizingEl.getWidth();
24718         }else{
24719             return s.resizingEl.getHeight();
24720         }
24721     },
24722     
24723     /**
24724      * Called after drag operations to set the size of the resizing element.
24725      * @param {Roo.SplitBar} s The SplitBar using this adapter
24726      * @param {Number} newSize The new size to set
24727      * @param {Function} onComplete A function to be invoked when resizing is complete
24728      */
24729     setElementSize : function(s, newSize, onComplete){
24730         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24731             if(!s.animate){
24732                 s.resizingEl.setWidth(newSize);
24733                 if(onComplete){
24734                     onComplete(s, newSize);
24735                 }
24736             }else{
24737                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24738             }
24739         }else{
24740             
24741             if(!s.animate){
24742                 s.resizingEl.setHeight(newSize);
24743                 if(onComplete){
24744                     onComplete(s, newSize);
24745                 }
24746             }else{
24747                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24748             }
24749         }
24750     }
24751 };
24752
24753 /** 
24754  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24755  * @extends Roo.SplitBar.BasicLayoutAdapter
24756  * Adapter that  moves the splitter element to align with the resized sizing element. 
24757  * Used with an absolute positioned SplitBar.
24758  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24759  * document.body, make sure you assign an id to the body element.
24760  */
24761 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24762     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24763     this.container = Roo.get(container);
24764 };
24765
24766 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24767     init : function(s){
24768         this.basic.init(s);
24769     },
24770     
24771     getElementSize : function(s){
24772         return this.basic.getElementSize(s);
24773     },
24774     
24775     setElementSize : function(s, newSize, onComplete){
24776         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24777     },
24778     
24779     moveSplitter : function(s){
24780         var yes = Roo.SplitBar;
24781         switch(s.placement){
24782             case yes.LEFT:
24783                 s.el.setX(s.resizingEl.getRight());
24784                 break;
24785             case yes.RIGHT:
24786                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24787                 break;
24788             case yes.TOP:
24789                 s.el.setY(s.resizingEl.getBottom());
24790                 break;
24791             case yes.BOTTOM:
24792                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24793                 break;
24794         }
24795     }
24796 };
24797
24798 /**
24799  * Orientation constant - Create a vertical SplitBar
24800  * @static
24801  * @type Number
24802  */
24803 Roo.SplitBar.VERTICAL = 1;
24804
24805 /**
24806  * Orientation constant - Create a horizontal SplitBar
24807  * @static
24808  * @type Number
24809  */
24810 Roo.SplitBar.HORIZONTAL = 2;
24811
24812 /**
24813  * Placement constant - The resizing element is to the left of the splitter element
24814  * @static
24815  * @type Number
24816  */
24817 Roo.SplitBar.LEFT = 1;
24818
24819 /**
24820  * Placement constant - The resizing element is to the right of the splitter element
24821  * @static
24822  * @type Number
24823  */
24824 Roo.SplitBar.RIGHT = 2;
24825
24826 /**
24827  * Placement constant - The resizing element is positioned above the splitter element
24828  * @static
24829  * @type Number
24830  */
24831 Roo.SplitBar.TOP = 3;
24832
24833 /**
24834  * Placement constant - The resizing element is positioned under splitter element
24835  * @static
24836  * @type Number
24837  */
24838 Roo.SplitBar.BOTTOM = 4;
24839 /*
24840  * Based on:
24841  * Ext JS Library 1.1.1
24842  * Copyright(c) 2006-2007, Ext JS, LLC.
24843  *
24844  * Originally Released Under LGPL - original licence link has changed is not relivant.
24845  *
24846  * Fork - LGPL
24847  * <script type="text/javascript">
24848  */
24849
24850 /**
24851  * @class Roo.View
24852  * @extends Roo.util.Observable
24853  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24854  * This class also supports single and multi selection modes. <br>
24855  * Create a data model bound view:
24856  <pre><code>
24857  var store = new Roo.data.Store(...);
24858
24859  var view = new Roo.View({
24860     el : "my-element",
24861     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24862  
24863     singleSelect: true,
24864     selectedClass: "ydataview-selected",
24865     store: store
24866  });
24867
24868  // listen for node click?
24869  view.on("click", function(vw, index, node, e){
24870  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24871  });
24872
24873  // load XML data
24874  dataModel.load("foobar.xml");
24875  </code></pre>
24876  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24877  * <br><br>
24878  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24879  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24880  * 
24881  * Note: old style constructor is still suported (container, template, config)
24882  * 
24883  * @constructor
24884  * Create a new View
24885  * @param {Object} config The config object
24886  * 
24887  */
24888 Roo.View = function(config, depreciated_tpl, depreciated_config){
24889     
24890     this.parent = false;
24891     
24892     if (typeof(depreciated_tpl) == 'undefined') {
24893         // new way.. - universal constructor.
24894         Roo.apply(this, config);
24895         this.el  = Roo.get(this.el);
24896     } else {
24897         // old format..
24898         this.el  = Roo.get(config);
24899         this.tpl = depreciated_tpl;
24900         Roo.apply(this, depreciated_config);
24901     }
24902     this.wrapEl  = this.el.wrap().wrap();
24903     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24904     
24905     
24906     if(typeof(this.tpl) == "string"){
24907         this.tpl = new Roo.Template(this.tpl);
24908     } else {
24909         // support xtype ctors..
24910         this.tpl = new Roo.factory(this.tpl, Roo);
24911     }
24912     
24913     
24914     this.tpl.compile();
24915     
24916     /** @private */
24917     this.addEvents({
24918         /**
24919          * @event beforeclick
24920          * Fires before a click is processed. Returns false to cancel the default action.
24921          * @param {Roo.View} this
24922          * @param {Number} index The index of the target node
24923          * @param {HTMLElement} node The target node
24924          * @param {Roo.EventObject} e The raw event object
24925          */
24926             "beforeclick" : true,
24927         /**
24928          * @event click
24929          * Fires when a template node is clicked.
24930          * @param {Roo.View} this
24931          * @param {Number} index The index of the target node
24932          * @param {HTMLElement} node The target node
24933          * @param {Roo.EventObject} e The raw event object
24934          */
24935             "click" : true,
24936         /**
24937          * @event dblclick
24938          * Fires when a template node is double clicked.
24939          * @param {Roo.View} this
24940          * @param {Number} index The index of the target node
24941          * @param {HTMLElement} node The target node
24942          * @param {Roo.EventObject} e The raw event object
24943          */
24944             "dblclick" : true,
24945         /**
24946          * @event contextmenu
24947          * Fires when a template node is right clicked.
24948          * @param {Roo.View} this
24949          * @param {Number} index The index of the target node
24950          * @param {HTMLElement} node The target node
24951          * @param {Roo.EventObject} e The raw event object
24952          */
24953             "contextmenu" : true,
24954         /**
24955          * @event selectionchange
24956          * Fires when the selected nodes change.
24957          * @param {Roo.View} this
24958          * @param {Array} selections Array of the selected nodes
24959          */
24960             "selectionchange" : true,
24961     
24962         /**
24963          * @event beforeselect
24964          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24965          * @param {Roo.View} this
24966          * @param {HTMLElement} node The node to be selected
24967          * @param {Array} selections Array of currently selected nodes
24968          */
24969             "beforeselect" : true,
24970         /**
24971          * @event preparedata
24972          * Fires on every row to render, to allow you to change the data.
24973          * @param {Roo.View} this
24974          * @param {Object} data to be rendered (change this)
24975          */
24976           "preparedata" : true
24977           
24978           
24979         });
24980
24981
24982
24983     this.el.on({
24984         "click": this.onClick,
24985         "dblclick": this.onDblClick,
24986         "contextmenu": this.onContextMenu,
24987         scope:this
24988     });
24989
24990     this.selections = [];
24991     this.nodes = [];
24992     this.cmp = new Roo.CompositeElementLite([]);
24993     if(this.store){
24994         this.store = Roo.factory(this.store, Roo.data);
24995         this.setStore(this.store, true);
24996     }
24997     
24998     if ( this.footer && this.footer.xtype) {
24999            
25000          var fctr = this.wrapEl.appendChild(document.createElement("div"));
25001         
25002         this.footer.dataSource = this.store;
25003         this.footer.container = fctr;
25004         this.footer = Roo.factory(this.footer, Roo);
25005         fctr.insertFirst(this.el);
25006         
25007         // this is a bit insane - as the paging toolbar seems to detach the el..
25008 //        dom.parentNode.parentNode.parentNode
25009          // they get detached?
25010     }
25011     
25012     
25013     Roo.View.superclass.constructor.call(this);
25014     
25015     
25016 };
25017
25018 Roo.extend(Roo.View, Roo.util.Observable, {
25019     
25020      /**
25021      * @cfg {Roo.data.Store} store Data store to load data from.
25022      */
25023     store : false,
25024     
25025     /**
25026      * @cfg {String|Roo.Element} el The container element.
25027      */
25028     el : '',
25029     
25030     /**
25031      * @cfg {String|Roo.Template} tpl The template used by this View 
25032      */
25033     tpl : false,
25034     /**
25035      * @cfg {String} dataName the named area of the template to use as the data area
25036      *                          Works with domtemplates roo-name="name"
25037      */
25038     dataName: false,
25039     /**
25040      * @cfg {String} selectedClass The css class to add to selected nodes
25041      */
25042     selectedClass : "x-view-selected",
25043      /**
25044      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25045      */
25046     emptyText : "",
25047     
25048     /**
25049      * @cfg {String} text to display on mask (default Loading)
25050      */
25051     mask : false,
25052     /**
25053      * @cfg {Boolean} multiSelect Allow multiple selection
25054      */
25055     multiSelect : false,
25056     /**
25057      * @cfg {Boolean} singleSelect Allow single selection
25058      */
25059     singleSelect:  false,
25060     
25061     /**
25062      * @cfg {Boolean} toggleSelect - selecting 
25063      */
25064     toggleSelect : false,
25065     
25066     /**
25067      * @cfg {Boolean} tickable - selecting 
25068      */
25069     tickable : false,
25070     
25071     /**
25072      * Returns the element this view is bound to.
25073      * @return {Roo.Element}
25074      */
25075     getEl : function(){
25076         return this.wrapEl;
25077     },
25078     
25079     
25080
25081     /**
25082      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25083      */
25084     refresh : function(){
25085         //Roo.log('refresh');
25086         var t = this.tpl;
25087         
25088         // if we are using something like 'domtemplate', then
25089         // the what gets used is:
25090         // t.applySubtemplate(NAME, data, wrapping data..)
25091         // the outer template then get' applied with
25092         //     the store 'extra data'
25093         // and the body get's added to the
25094         //      roo-name="data" node?
25095         //      <span class='roo-tpl-{name}'></span> ?????
25096         
25097         
25098         
25099         this.clearSelections();
25100         this.el.update("");
25101         var html = [];
25102         var records = this.store.getRange();
25103         if(records.length < 1) {
25104             
25105             // is this valid??  = should it render a template??
25106             
25107             this.el.update(this.emptyText);
25108             return;
25109         }
25110         var el = this.el;
25111         if (this.dataName) {
25112             this.el.update(t.apply(this.store.meta)); //????
25113             el = this.el.child('.roo-tpl-' + this.dataName);
25114         }
25115         
25116         for(var i = 0, len = records.length; i < len; i++){
25117             var data = this.prepareData(records[i].data, i, records[i]);
25118             this.fireEvent("preparedata", this, data, i, records[i]);
25119             
25120             var d = Roo.apply({}, data);
25121             
25122             if(this.tickable){
25123                 Roo.apply(d, {'roo-id' : Roo.id()});
25124                 
25125                 var _this = this;
25126             
25127                 Roo.each(this.parent.item, function(item){
25128                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25129                         return;
25130                     }
25131                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25132                 });
25133             }
25134             
25135             html[html.length] = Roo.util.Format.trim(
25136                 this.dataName ?
25137                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25138                     t.apply(d)
25139             );
25140         }
25141         
25142         
25143         
25144         el.update(html.join(""));
25145         this.nodes = el.dom.childNodes;
25146         this.updateIndexes(0);
25147     },
25148     
25149
25150     /**
25151      * Function to override to reformat the data that is sent to
25152      * the template for each node.
25153      * DEPRICATED - use the preparedata event handler.
25154      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25155      * a JSON object for an UpdateManager bound view).
25156      */
25157     prepareData : function(data, index, record)
25158     {
25159         this.fireEvent("preparedata", this, data, index, record);
25160         return data;
25161     },
25162
25163     onUpdate : function(ds, record){
25164         // Roo.log('on update');   
25165         this.clearSelections();
25166         var index = this.store.indexOf(record);
25167         var n = this.nodes[index];
25168         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25169         n.parentNode.removeChild(n);
25170         this.updateIndexes(index, index);
25171     },
25172
25173     
25174     
25175 // --------- FIXME     
25176     onAdd : function(ds, records, index)
25177     {
25178         //Roo.log(['on Add', ds, records, index] );        
25179         this.clearSelections();
25180         if(this.nodes.length == 0){
25181             this.refresh();
25182             return;
25183         }
25184         var n = this.nodes[index];
25185         for(var i = 0, len = records.length; i < len; i++){
25186             var d = this.prepareData(records[i].data, i, records[i]);
25187             if(n){
25188                 this.tpl.insertBefore(n, d);
25189             }else{
25190                 
25191                 this.tpl.append(this.el, d);
25192             }
25193         }
25194         this.updateIndexes(index);
25195     },
25196
25197     onRemove : function(ds, record, index){
25198        // Roo.log('onRemove');
25199         this.clearSelections();
25200         var el = this.dataName  ?
25201             this.el.child('.roo-tpl-' + this.dataName) :
25202             this.el; 
25203         
25204         el.dom.removeChild(this.nodes[index]);
25205         this.updateIndexes(index);
25206     },
25207
25208     /**
25209      * Refresh an individual node.
25210      * @param {Number} index
25211      */
25212     refreshNode : function(index){
25213         this.onUpdate(this.store, this.store.getAt(index));
25214     },
25215
25216     updateIndexes : function(startIndex, endIndex){
25217         var ns = this.nodes;
25218         startIndex = startIndex || 0;
25219         endIndex = endIndex || ns.length - 1;
25220         for(var i = startIndex; i <= endIndex; i++){
25221             ns[i].nodeIndex = i;
25222         }
25223     },
25224
25225     /**
25226      * Changes the data store this view uses and refresh the view.
25227      * @param {Store} store
25228      */
25229     setStore : function(store, initial){
25230         if(!initial && this.store){
25231             this.store.un("datachanged", this.refresh);
25232             this.store.un("add", this.onAdd);
25233             this.store.un("remove", this.onRemove);
25234             this.store.un("update", this.onUpdate);
25235             this.store.un("clear", this.refresh);
25236             this.store.un("beforeload", this.onBeforeLoad);
25237             this.store.un("load", this.onLoad);
25238             this.store.un("loadexception", this.onLoad);
25239         }
25240         if(store){
25241           
25242             store.on("datachanged", this.refresh, this);
25243             store.on("add", this.onAdd, this);
25244             store.on("remove", this.onRemove, this);
25245             store.on("update", this.onUpdate, this);
25246             store.on("clear", this.refresh, this);
25247             store.on("beforeload", this.onBeforeLoad, this);
25248             store.on("load", this.onLoad, this);
25249             store.on("loadexception", this.onLoad, this);
25250         }
25251         
25252         if(store){
25253             this.refresh();
25254         }
25255     },
25256     /**
25257      * onbeforeLoad - masks the loading area.
25258      *
25259      */
25260     onBeforeLoad : function(store,opts)
25261     {
25262          //Roo.log('onBeforeLoad');   
25263         if (!opts.add) {
25264             this.el.update("");
25265         }
25266         this.el.mask(this.mask ? this.mask : "Loading" ); 
25267     },
25268     onLoad : function ()
25269     {
25270         this.el.unmask();
25271     },
25272     
25273
25274     /**
25275      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25276      * @param {HTMLElement} node
25277      * @return {HTMLElement} The template node
25278      */
25279     findItemFromChild : function(node){
25280         var el = this.dataName  ?
25281             this.el.child('.roo-tpl-' + this.dataName,true) :
25282             this.el.dom; 
25283         
25284         if(!node || node.parentNode == el){
25285                     return node;
25286             }
25287             var p = node.parentNode;
25288             while(p && p != el){
25289             if(p.parentNode == el){
25290                 return p;
25291             }
25292             p = p.parentNode;
25293         }
25294             return null;
25295     },
25296
25297     /** @ignore */
25298     onClick : function(e){
25299         var item = this.findItemFromChild(e.getTarget());
25300         if(item){
25301             var index = this.indexOf(item);
25302             if(this.onItemClick(item, index, e) !== false){
25303                 this.fireEvent("click", this, index, item, e);
25304             }
25305         }else{
25306             this.clearSelections();
25307         }
25308     },
25309
25310     /** @ignore */
25311     onContextMenu : function(e){
25312         var item = this.findItemFromChild(e.getTarget());
25313         if(item){
25314             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25315         }
25316     },
25317
25318     /** @ignore */
25319     onDblClick : function(e){
25320         var item = this.findItemFromChild(e.getTarget());
25321         if(item){
25322             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25323         }
25324     },
25325
25326     onItemClick : function(item, index, e)
25327     {
25328         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25329             return false;
25330         }
25331         if (this.toggleSelect) {
25332             var m = this.isSelected(item) ? 'unselect' : 'select';
25333             //Roo.log(m);
25334             var _t = this;
25335             _t[m](item, true, false);
25336             return true;
25337         }
25338         if(this.multiSelect || this.singleSelect){
25339             if(this.multiSelect && e.shiftKey && this.lastSelection){
25340                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25341             }else{
25342                 this.select(item, this.multiSelect && e.ctrlKey);
25343                 this.lastSelection = item;
25344             }
25345             
25346             if(!this.tickable){
25347                 e.preventDefault();
25348             }
25349             
25350         }
25351         return true;
25352     },
25353
25354     /**
25355      * Get the number of selected nodes.
25356      * @return {Number}
25357      */
25358     getSelectionCount : function(){
25359         return this.selections.length;
25360     },
25361
25362     /**
25363      * Get the currently selected nodes.
25364      * @return {Array} An array of HTMLElements
25365      */
25366     getSelectedNodes : function(){
25367         return this.selections;
25368     },
25369
25370     /**
25371      * Get the indexes of the selected nodes.
25372      * @return {Array}
25373      */
25374     getSelectedIndexes : function(){
25375         var indexes = [], s = this.selections;
25376         for(var i = 0, len = s.length; i < len; i++){
25377             indexes.push(s[i].nodeIndex);
25378         }
25379         return indexes;
25380     },
25381
25382     /**
25383      * Clear all selections
25384      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25385      */
25386     clearSelections : function(suppressEvent){
25387         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25388             this.cmp.elements = this.selections;
25389             this.cmp.removeClass(this.selectedClass);
25390             this.selections = [];
25391             if(!suppressEvent){
25392                 this.fireEvent("selectionchange", this, this.selections);
25393             }
25394         }
25395     },
25396
25397     /**
25398      * Returns true if the passed node is selected
25399      * @param {HTMLElement/Number} node The node or node index
25400      * @return {Boolean}
25401      */
25402     isSelected : function(node){
25403         var s = this.selections;
25404         if(s.length < 1){
25405             return false;
25406         }
25407         node = this.getNode(node);
25408         return s.indexOf(node) !== -1;
25409     },
25410
25411     /**
25412      * Selects nodes.
25413      * @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
25414      * @param {Boolean} keepExisting (optional) true to keep existing selections
25415      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25416      */
25417     select : function(nodeInfo, keepExisting, suppressEvent){
25418         if(nodeInfo instanceof Array){
25419             if(!keepExisting){
25420                 this.clearSelections(true);
25421             }
25422             for(var i = 0, len = nodeInfo.length; i < len; i++){
25423                 this.select(nodeInfo[i], true, true);
25424             }
25425             return;
25426         } 
25427         var node = this.getNode(nodeInfo);
25428         if(!node || this.isSelected(node)){
25429             return; // already selected.
25430         }
25431         if(!keepExisting){
25432             this.clearSelections(true);
25433         }
25434         
25435         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25436             Roo.fly(node).addClass(this.selectedClass);
25437             this.selections.push(node);
25438             if(!suppressEvent){
25439                 this.fireEvent("selectionchange", this, this.selections);
25440             }
25441         }
25442         
25443         
25444     },
25445       /**
25446      * Unselects nodes.
25447      * @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
25448      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25449      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25450      */
25451     unselect : function(nodeInfo, keepExisting, suppressEvent)
25452     {
25453         if(nodeInfo instanceof Array){
25454             Roo.each(this.selections, function(s) {
25455                 this.unselect(s, nodeInfo);
25456             }, this);
25457             return;
25458         }
25459         var node = this.getNode(nodeInfo);
25460         if(!node || !this.isSelected(node)){
25461             //Roo.log("not selected");
25462             return; // not selected.
25463         }
25464         // fireevent???
25465         var ns = [];
25466         Roo.each(this.selections, function(s) {
25467             if (s == node ) {
25468                 Roo.fly(node).removeClass(this.selectedClass);
25469
25470                 return;
25471             }
25472             ns.push(s);
25473         },this);
25474         
25475         this.selections= ns;
25476         this.fireEvent("selectionchange", this, this.selections);
25477     },
25478
25479     /**
25480      * Gets a template node.
25481      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25482      * @return {HTMLElement} The node or null if it wasn't found
25483      */
25484     getNode : function(nodeInfo){
25485         if(typeof nodeInfo == "string"){
25486             return document.getElementById(nodeInfo);
25487         }else if(typeof nodeInfo == "number"){
25488             return this.nodes[nodeInfo];
25489         }
25490         return nodeInfo;
25491     },
25492
25493     /**
25494      * Gets a range template nodes.
25495      * @param {Number} startIndex
25496      * @param {Number} endIndex
25497      * @return {Array} An array of nodes
25498      */
25499     getNodes : function(start, end){
25500         var ns = this.nodes;
25501         start = start || 0;
25502         end = typeof end == "undefined" ? ns.length - 1 : end;
25503         var nodes = [];
25504         if(start <= end){
25505             for(var i = start; i <= end; i++){
25506                 nodes.push(ns[i]);
25507             }
25508         } else{
25509             for(var i = start; i >= end; i--){
25510                 nodes.push(ns[i]);
25511             }
25512         }
25513         return nodes;
25514     },
25515
25516     /**
25517      * Finds the index of the passed node
25518      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25519      * @return {Number} The index of the node or -1
25520      */
25521     indexOf : function(node){
25522         node = this.getNode(node);
25523         if(typeof node.nodeIndex == "number"){
25524             return node.nodeIndex;
25525         }
25526         var ns = this.nodes;
25527         for(var i = 0, len = ns.length; i < len; i++){
25528             if(ns[i] == node){
25529                 return i;
25530             }
25531         }
25532         return -1;
25533     }
25534 });
25535 /*
25536  * Based on:
25537  * Ext JS Library 1.1.1
25538  * Copyright(c) 2006-2007, Ext JS, LLC.
25539  *
25540  * Originally Released Under LGPL - original licence link has changed is not relivant.
25541  *
25542  * Fork - LGPL
25543  * <script type="text/javascript">
25544  */
25545
25546 /**
25547  * @class Roo.JsonView
25548  * @extends Roo.View
25549  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25550 <pre><code>
25551 var view = new Roo.JsonView({
25552     container: "my-element",
25553     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25554     multiSelect: true, 
25555     jsonRoot: "data" 
25556 });
25557
25558 // listen for node click?
25559 view.on("click", function(vw, index, node, e){
25560     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25561 });
25562
25563 // direct load of JSON data
25564 view.load("foobar.php");
25565
25566 // Example from my blog list
25567 var tpl = new Roo.Template(
25568     '&lt;div class="entry"&gt;' +
25569     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25570     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25571     "&lt;/div&gt;&lt;hr /&gt;"
25572 );
25573
25574 var moreView = new Roo.JsonView({
25575     container :  "entry-list", 
25576     template : tpl,
25577     jsonRoot: "posts"
25578 });
25579 moreView.on("beforerender", this.sortEntries, this);
25580 moreView.load({
25581     url: "/blog/get-posts.php",
25582     params: "allposts=true",
25583     text: "Loading Blog Entries..."
25584 });
25585 </code></pre>
25586
25587 * Note: old code is supported with arguments : (container, template, config)
25588
25589
25590  * @constructor
25591  * Create a new JsonView
25592  * 
25593  * @param {Object} config The config object
25594  * 
25595  */
25596 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25597     
25598     
25599     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25600
25601     var um = this.el.getUpdateManager();
25602     um.setRenderer(this);
25603     um.on("update", this.onLoad, this);
25604     um.on("failure", this.onLoadException, this);
25605
25606     /**
25607      * @event beforerender
25608      * Fires before rendering of the downloaded JSON data.
25609      * @param {Roo.JsonView} this
25610      * @param {Object} data The JSON data loaded
25611      */
25612     /**
25613      * @event load
25614      * Fires when data is loaded.
25615      * @param {Roo.JsonView} this
25616      * @param {Object} data The JSON data loaded
25617      * @param {Object} response The raw Connect response object
25618      */
25619     /**
25620      * @event loadexception
25621      * Fires when loading fails.
25622      * @param {Roo.JsonView} this
25623      * @param {Object} response The raw Connect response object
25624      */
25625     this.addEvents({
25626         'beforerender' : true,
25627         'load' : true,
25628         'loadexception' : true
25629     });
25630 };
25631 Roo.extend(Roo.JsonView, Roo.View, {
25632     /**
25633      * @type {String} The root property in the loaded JSON object that contains the data
25634      */
25635     jsonRoot : "",
25636
25637     /**
25638      * Refreshes the view.
25639      */
25640     refresh : function(){
25641         this.clearSelections();
25642         this.el.update("");
25643         var html = [];
25644         var o = this.jsonData;
25645         if(o && o.length > 0){
25646             for(var i = 0, len = o.length; i < len; i++){
25647                 var data = this.prepareData(o[i], i, o);
25648                 html[html.length] = this.tpl.apply(data);
25649             }
25650         }else{
25651             html.push(this.emptyText);
25652         }
25653         this.el.update(html.join(""));
25654         this.nodes = this.el.dom.childNodes;
25655         this.updateIndexes(0);
25656     },
25657
25658     /**
25659      * 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.
25660      * @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:
25661      <pre><code>
25662      view.load({
25663          url: "your-url.php",
25664          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25665          callback: yourFunction,
25666          scope: yourObject, //(optional scope)
25667          discardUrl: false,
25668          nocache: false,
25669          text: "Loading...",
25670          timeout: 30,
25671          scripts: false
25672      });
25673      </code></pre>
25674      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25675      * 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.
25676      * @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}
25677      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25678      * @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.
25679      */
25680     load : function(){
25681         var um = this.el.getUpdateManager();
25682         um.update.apply(um, arguments);
25683     },
25684
25685     render : function(el, response){
25686         this.clearSelections();
25687         this.el.update("");
25688         var o;
25689         try{
25690             o = Roo.util.JSON.decode(response.responseText);
25691             if(this.jsonRoot){
25692                 
25693                 o = o[this.jsonRoot];
25694             }
25695         } catch(e){
25696         }
25697         /**
25698          * The current JSON data or null
25699          */
25700         this.jsonData = o;
25701         this.beforeRender();
25702         this.refresh();
25703     },
25704
25705 /**
25706  * Get the number of records in the current JSON dataset
25707  * @return {Number}
25708  */
25709     getCount : function(){
25710         return this.jsonData ? this.jsonData.length : 0;
25711     },
25712
25713 /**
25714  * Returns the JSON object for the specified node(s)
25715  * @param {HTMLElement/Array} node The node or an array of nodes
25716  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25717  * you get the JSON object for the node
25718  */
25719     getNodeData : function(node){
25720         if(node instanceof Array){
25721             var data = [];
25722             for(var i = 0, len = node.length; i < len; i++){
25723                 data.push(this.getNodeData(node[i]));
25724             }
25725             return data;
25726         }
25727         return this.jsonData[this.indexOf(node)] || null;
25728     },
25729
25730     beforeRender : function(){
25731         this.snapshot = this.jsonData;
25732         if(this.sortInfo){
25733             this.sort.apply(this, this.sortInfo);
25734         }
25735         this.fireEvent("beforerender", this, this.jsonData);
25736     },
25737
25738     onLoad : function(el, o){
25739         this.fireEvent("load", this, this.jsonData, o);
25740     },
25741
25742     onLoadException : function(el, o){
25743         this.fireEvent("loadexception", this, o);
25744     },
25745
25746 /**
25747  * Filter the data by a specific property.
25748  * @param {String} property A property on your JSON objects
25749  * @param {String/RegExp} value Either string that the property values
25750  * should start with, or a RegExp to test against the property
25751  */
25752     filter : function(property, value){
25753         if(this.jsonData){
25754             var data = [];
25755             var ss = this.snapshot;
25756             if(typeof value == "string"){
25757                 var vlen = value.length;
25758                 if(vlen == 0){
25759                     this.clearFilter();
25760                     return;
25761                 }
25762                 value = value.toLowerCase();
25763                 for(var i = 0, len = ss.length; i < len; i++){
25764                     var o = ss[i];
25765                     if(o[property].substr(0, vlen).toLowerCase() == value){
25766                         data.push(o);
25767                     }
25768                 }
25769             } else if(value.exec){ // regex?
25770                 for(var i = 0, len = ss.length; i < len; i++){
25771                     var o = ss[i];
25772                     if(value.test(o[property])){
25773                         data.push(o);
25774                     }
25775                 }
25776             } else{
25777                 return;
25778             }
25779             this.jsonData = data;
25780             this.refresh();
25781         }
25782     },
25783
25784 /**
25785  * Filter by a function. The passed function will be called with each
25786  * object in the current dataset. If the function returns true the value is kept,
25787  * otherwise it is filtered.
25788  * @param {Function} fn
25789  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25790  */
25791     filterBy : function(fn, scope){
25792         if(this.jsonData){
25793             var data = [];
25794             var ss = this.snapshot;
25795             for(var i = 0, len = ss.length; i < len; i++){
25796                 var o = ss[i];
25797                 if(fn.call(scope || this, o)){
25798                     data.push(o);
25799                 }
25800             }
25801             this.jsonData = data;
25802             this.refresh();
25803         }
25804     },
25805
25806 /**
25807  * Clears the current filter.
25808  */
25809     clearFilter : function(){
25810         if(this.snapshot && this.jsonData != this.snapshot){
25811             this.jsonData = this.snapshot;
25812             this.refresh();
25813         }
25814     },
25815
25816
25817 /**
25818  * Sorts the data for this view and refreshes it.
25819  * @param {String} property A property on your JSON objects to sort on
25820  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25821  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25822  */
25823     sort : function(property, dir, sortType){
25824         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25825         if(this.jsonData){
25826             var p = property;
25827             var dsc = dir && dir.toLowerCase() == "desc";
25828             var f = function(o1, o2){
25829                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25830                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25831                 ;
25832                 if(v1 < v2){
25833                     return dsc ? +1 : -1;
25834                 } else if(v1 > v2){
25835                     return dsc ? -1 : +1;
25836                 } else{
25837                     return 0;
25838                 }
25839             };
25840             this.jsonData.sort(f);
25841             this.refresh();
25842             if(this.jsonData != this.snapshot){
25843                 this.snapshot.sort(f);
25844             }
25845         }
25846     }
25847 });/*
25848  * Based on:
25849  * Ext JS Library 1.1.1
25850  * Copyright(c) 2006-2007, Ext JS, LLC.
25851  *
25852  * Originally Released Under LGPL - original licence link has changed is not relivant.
25853  *
25854  * Fork - LGPL
25855  * <script type="text/javascript">
25856  */
25857  
25858
25859 /**
25860  * @class Roo.ColorPalette
25861  * @extends Roo.Component
25862  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25863  * Here's an example of typical usage:
25864  * <pre><code>
25865 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25866 cp.render('my-div');
25867
25868 cp.on('select', function(palette, selColor){
25869     // do something with selColor
25870 });
25871 </code></pre>
25872  * @constructor
25873  * Create a new ColorPalette
25874  * @param {Object} config The config object
25875  */
25876 Roo.ColorPalette = function(config){
25877     Roo.ColorPalette.superclass.constructor.call(this, config);
25878     this.addEvents({
25879         /**
25880              * @event select
25881              * Fires when a color is selected
25882              * @param {ColorPalette} this
25883              * @param {String} color The 6-digit color hex code (without the # symbol)
25884              */
25885         select: true
25886     });
25887
25888     if(this.handler){
25889         this.on("select", this.handler, this.scope, true);
25890     }
25891 };
25892 Roo.extend(Roo.ColorPalette, Roo.Component, {
25893     /**
25894      * @cfg {String} itemCls
25895      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25896      */
25897     itemCls : "x-color-palette",
25898     /**
25899      * @cfg {String} value
25900      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25901      * the hex codes are case-sensitive.
25902      */
25903     value : null,
25904     clickEvent:'click',
25905     // private
25906     ctype: "Roo.ColorPalette",
25907
25908     /**
25909      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25910      */
25911     allowReselect : false,
25912
25913     /**
25914      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25915      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25916      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25917      * of colors with the width setting until the box is symmetrical.</p>
25918      * <p>You can override individual colors if needed:</p>
25919      * <pre><code>
25920 var cp = new Roo.ColorPalette();
25921 cp.colors[0] = "FF0000";  // change the first box to red
25922 </code></pre>
25923
25924 Or you can provide a custom array of your own for complete control:
25925 <pre><code>
25926 var cp = new Roo.ColorPalette();
25927 cp.colors = ["000000", "993300", "333300"];
25928 </code></pre>
25929      * @type Array
25930      */
25931     colors : [
25932         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25933         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25934         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25935         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25936         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25937     ],
25938
25939     // private
25940     onRender : function(container, position){
25941         var t = new Roo.MasterTemplate(
25942             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25943         );
25944         var c = this.colors;
25945         for(var i = 0, len = c.length; i < len; i++){
25946             t.add([c[i]]);
25947         }
25948         var el = document.createElement("div");
25949         el.className = this.itemCls;
25950         t.overwrite(el);
25951         container.dom.insertBefore(el, position);
25952         this.el = Roo.get(el);
25953         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25954         if(this.clickEvent != 'click'){
25955             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25956         }
25957     },
25958
25959     // private
25960     afterRender : function(){
25961         Roo.ColorPalette.superclass.afterRender.call(this);
25962         if(this.value){
25963             var s = this.value;
25964             this.value = null;
25965             this.select(s);
25966         }
25967     },
25968
25969     // private
25970     handleClick : function(e, t){
25971         e.preventDefault();
25972         if(!this.disabled){
25973             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25974             this.select(c.toUpperCase());
25975         }
25976     },
25977
25978     /**
25979      * Selects the specified color in the palette (fires the select event)
25980      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25981      */
25982     select : function(color){
25983         color = color.replace("#", "");
25984         if(color != this.value || this.allowReselect){
25985             var el = this.el;
25986             if(this.value){
25987                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25988             }
25989             el.child("a.color-"+color).addClass("x-color-palette-sel");
25990             this.value = color;
25991             this.fireEvent("select", this, color);
25992         }
25993     }
25994 });/*
25995  * Based on:
25996  * Ext JS Library 1.1.1
25997  * Copyright(c) 2006-2007, Ext JS, LLC.
25998  *
25999  * Originally Released Under LGPL - original licence link has changed is not relivant.
26000  *
26001  * Fork - LGPL
26002  * <script type="text/javascript">
26003  */
26004  
26005 /**
26006  * @class Roo.DatePicker
26007  * @extends Roo.Component
26008  * Simple date picker class.
26009  * @constructor
26010  * Create a new DatePicker
26011  * @param {Object} config The config object
26012  */
26013 Roo.DatePicker = function(config){
26014     Roo.DatePicker.superclass.constructor.call(this, config);
26015
26016     this.value = config && config.value ?
26017                  config.value.clearTime() : new Date().clearTime();
26018
26019     this.addEvents({
26020         /**
26021              * @event select
26022              * Fires when a date is selected
26023              * @param {DatePicker} this
26024              * @param {Date} date The selected date
26025              */
26026         'select': true,
26027         /**
26028              * @event monthchange
26029              * Fires when the displayed month changes 
26030              * @param {DatePicker} this
26031              * @param {Date} date The selected month
26032              */
26033         'monthchange': true
26034     });
26035
26036     if(this.handler){
26037         this.on("select", this.handler,  this.scope || this);
26038     }
26039     // build the disabledDatesRE
26040     if(!this.disabledDatesRE && this.disabledDates){
26041         var dd = this.disabledDates;
26042         var re = "(?:";
26043         for(var i = 0; i < dd.length; i++){
26044             re += dd[i];
26045             if(i != dd.length-1) re += "|";
26046         }
26047         this.disabledDatesRE = new RegExp(re + ")");
26048     }
26049 };
26050
26051 Roo.extend(Roo.DatePicker, Roo.Component, {
26052     /**
26053      * @cfg {String} todayText
26054      * The text to display on the button that selects the current date (defaults to "Today")
26055      */
26056     todayText : "Today",
26057     /**
26058      * @cfg {String} okText
26059      * The text to display on the ok button
26060      */
26061     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26062     /**
26063      * @cfg {String} cancelText
26064      * The text to display on the cancel button
26065      */
26066     cancelText : "Cancel",
26067     /**
26068      * @cfg {String} todayTip
26069      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26070      */
26071     todayTip : "{0} (Spacebar)",
26072     /**
26073      * @cfg {Date} minDate
26074      * Minimum allowable date (JavaScript date object, defaults to null)
26075      */
26076     minDate : null,
26077     /**
26078      * @cfg {Date} maxDate
26079      * Maximum allowable date (JavaScript date object, defaults to null)
26080      */
26081     maxDate : null,
26082     /**
26083      * @cfg {String} minText
26084      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26085      */
26086     minText : "This date is before the minimum date",
26087     /**
26088      * @cfg {String} maxText
26089      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26090      */
26091     maxText : "This date is after the maximum date",
26092     /**
26093      * @cfg {String} format
26094      * The default date format string which can be overriden for localization support.  The format must be
26095      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26096      */
26097     format : "m/d/y",
26098     /**
26099      * @cfg {Array} disabledDays
26100      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26101      */
26102     disabledDays : null,
26103     /**
26104      * @cfg {String} disabledDaysText
26105      * The tooltip to display when the date falls on a disabled day (defaults to "")
26106      */
26107     disabledDaysText : "",
26108     /**
26109      * @cfg {RegExp} disabledDatesRE
26110      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26111      */
26112     disabledDatesRE : null,
26113     /**
26114      * @cfg {String} disabledDatesText
26115      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26116      */
26117     disabledDatesText : "",
26118     /**
26119      * @cfg {Boolean} constrainToViewport
26120      * True to constrain the date picker to the viewport (defaults to true)
26121      */
26122     constrainToViewport : true,
26123     /**
26124      * @cfg {Array} monthNames
26125      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26126      */
26127     monthNames : Date.monthNames,
26128     /**
26129      * @cfg {Array} dayNames
26130      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26131      */
26132     dayNames : Date.dayNames,
26133     /**
26134      * @cfg {String} nextText
26135      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26136      */
26137     nextText: 'Next Month (Control+Right)',
26138     /**
26139      * @cfg {String} prevText
26140      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26141      */
26142     prevText: 'Previous Month (Control+Left)',
26143     /**
26144      * @cfg {String} monthYearText
26145      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26146      */
26147     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26148     /**
26149      * @cfg {Number} startDay
26150      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26151      */
26152     startDay : 0,
26153     /**
26154      * @cfg {Bool} showClear
26155      * Show a clear button (usefull for date form elements that can be blank.)
26156      */
26157     
26158     showClear: false,
26159     
26160     /**
26161      * Sets the value of the date field
26162      * @param {Date} value The date to set
26163      */
26164     setValue : function(value){
26165         var old = this.value;
26166         
26167         if (typeof(value) == 'string') {
26168          
26169             value = Date.parseDate(value, this.format);
26170         }
26171         if (!value) {
26172             value = new Date();
26173         }
26174         
26175         this.value = value.clearTime(true);
26176         if(this.el){
26177             this.update(this.value);
26178         }
26179     },
26180
26181     /**
26182      * Gets the current selected value of the date field
26183      * @return {Date} The selected date
26184      */
26185     getValue : function(){
26186         return this.value;
26187     },
26188
26189     // private
26190     focus : function(){
26191         if(this.el){
26192             this.update(this.activeDate);
26193         }
26194     },
26195
26196     // privateval
26197     onRender : function(container, position){
26198         
26199         var m = [
26200              '<table cellspacing="0">',
26201                 '<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>',
26202                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26203         var dn = this.dayNames;
26204         for(var i = 0; i < 7; i++){
26205             var d = this.startDay+i;
26206             if(d > 6){
26207                 d = d-7;
26208             }
26209             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26210         }
26211         m[m.length] = "</tr></thead><tbody><tr>";
26212         for(var i = 0; i < 42; i++) {
26213             if(i % 7 == 0 && i != 0){
26214                 m[m.length] = "</tr><tr>";
26215             }
26216             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26217         }
26218         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26219             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26220
26221         var el = document.createElement("div");
26222         el.className = "x-date-picker";
26223         el.innerHTML = m.join("");
26224
26225         container.dom.insertBefore(el, position);
26226
26227         this.el = Roo.get(el);
26228         this.eventEl = Roo.get(el.firstChild);
26229
26230         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26231             handler: this.showPrevMonth,
26232             scope: this,
26233             preventDefault:true,
26234             stopDefault:true
26235         });
26236
26237         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26238             handler: this.showNextMonth,
26239             scope: this,
26240             preventDefault:true,
26241             stopDefault:true
26242         });
26243
26244         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26245
26246         this.monthPicker = this.el.down('div.x-date-mp');
26247         this.monthPicker.enableDisplayMode('block');
26248         
26249         var kn = new Roo.KeyNav(this.eventEl, {
26250             "left" : function(e){
26251                 e.ctrlKey ?
26252                     this.showPrevMonth() :
26253                     this.update(this.activeDate.add("d", -1));
26254             },
26255
26256             "right" : function(e){
26257                 e.ctrlKey ?
26258                     this.showNextMonth() :
26259                     this.update(this.activeDate.add("d", 1));
26260             },
26261
26262             "up" : function(e){
26263                 e.ctrlKey ?
26264                     this.showNextYear() :
26265                     this.update(this.activeDate.add("d", -7));
26266             },
26267
26268             "down" : function(e){
26269                 e.ctrlKey ?
26270                     this.showPrevYear() :
26271                     this.update(this.activeDate.add("d", 7));
26272             },
26273
26274             "pageUp" : function(e){
26275                 this.showNextMonth();
26276             },
26277
26278             "pageDown" : function(e){
26279                 this.showPrevMonth();
26280             },
26281
26282             "enter" : function(e){
26283                 e.stopPropagation();
26284                 return true;
26285             },
26286
26287             scope : this
26288         });
26289
26290         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26291
26292         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26293
26294         this.el.unselectable();
26295         
26296         this.cells = this.el.select("table.x-date-inner tbody td");
26297         this.textNodes = this.el.query("table.x-date-inner tbody span");
26298
26299         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26300             text: "&#160;",
26301             tooltip: this.monthYearText
26302         });
26303
26304         this.mbtn.on('click', this.showMonthPicker, this);
26305         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26306
26307
26308         var today = (new Date()).dateFormat(this.format);
26309         
26310         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26311         if (this.showClear) {
26312             baseTb.add( new Roo.Toolbar.Fill());
26313         }
26314         baseTb.add({
26315             text: String.format(this.todayText, today),
26316             tooltip: String.format(this.todayTip, today),
26317             handler: this.selectToday,
26318             scope: this
26319         });
26320         
26321         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26322             
26323         //});
26324         if (this.showClear) {
26325             
26326             baseTb.add( new Roo.Toolbar.Fill());
26327             baseTb.add({
26328                 text: '&#160;',
26329                 cls: 'x-btn-icon x-btn-clear',
26330                 handler: function() {
26331                     //this.value = '';
26332                     this.fireEvent("select", this, '');
26333                 },
26334                 scope: this
26335             });
26336         }
26337         
26338         
26339         if(Roo.isIE){
26340             this.el.repaint();
26341         }
26342         this.update(this.value);
26343     },
26344
26345     createMonthPicker : function(){
26346         if(!this.monthPicker.dom.firstChild){
26347             var buf = ['<table border="0" cellspacing="0">'];
26348             for(var i = 0; i < 6; i++){
26349                 buf.push(
26350                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26351                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26352                     i == 0 ?
26353                     '<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>' :
26354                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26355                 );
26356             }
26357             buf.push(
26358                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26359                     this.okText,
26360                     '</button><button type="button" class="x-date-mp-cancel">',
26361                     this.cancelText,
26362                     '</button></td></tr>',
26363                 '</table>'
26364             );
26365             this.monthPicker.update(buf.join(''));
26366             this.monthPicker.on('click', this.onMonthClick, this);
26367             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26368
26369             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26370             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26371
26372             this.mpMonths.each(function(m, a, i){
26373                 i += 1;
26374                 if((i%2) == 0){
26375                     m.dom.xmonth = 5 + Math.round(i * .5);
26376                 }else{
26377                     m.dom.xmonth = Math.round((i-1) * .5);
26378                 }
26379             });
26380         }
26381     },
26382
26383     showMonthPicker : function(){
26384         this.createMonthPicker();
26385         var size = this.el.getSize();
26386         this.monthPicker.setSize(size);
26387         this.monthPicker.child('table').setSize(size);
26388
26389         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26390         this.updateMPMonth(this.mpSelMonth);
26391         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26392         this.updateMPYear(this.mpSelYear);
26393
26394         this.monthPicker.slideIn('t', {duration:.2});
26395     },
26396
26397     updateMPYear : function(y){
26398         this.mpyear = y;
26399         var ys = this.mpYears.elements;
26400         for(var i = 1; i <= 10; i++){
26401             var td = ys[i-1], y2;
26402             if((i%2) == 0){
26403                 y2 = y + Math.round(i * .5);
26404                 td.firstChild.innerHTML = y2;
26405                 td.xyear = y2;
26406             }else{
26407                 y2 = y - (5-Math.round(i * .5));
26408                 td.firstChild.innerHTML = y2;
26409                 td.xyear = y2;
26410             }
26411             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26412         }
26413     },
26414
26415     updateMPMonth : function(sm){
26416         this.mpMonths.each(function(m, a, i){
26417             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26418         });
26419     },
26420
26421     selectMPMonth: function(m){
26422         
26423     },
26424
26425     onMonthClick : function(e, t){
26426         e.stopEvent();
26427         var el = new Roo.Element(t), pn;
26428         if(el.is('button.x-date-mp-cancel')){
26429             this.hideMonthPicker();
26430         }
26431         else if(el.is('button.x-date-mp-ok')){
26432             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26433             this.hideMonthPicker();
26434         }
26435         else if(pn = el.up('td.x-date-mp-month', 2)){
26436             this.mpMonths.removeClass('x-date-mp-sel');
26437             pn.addClass('x-date-mp-sel');
26438             this.mpSelMonth = pn.dom.xmonth;
26439         }
26440         else if(pn = el.up('td.x-date-mp-year', 2)){
26441             this.mpYears.removeClass('x-date-mp-sel');
26442             pn.addClass('x-date-mp-sel');
26443             this.mpSelYear = pn.dom.xyear;
26444         }
26445         else if(el.is('a.x-date-mp-prev')){
26446             this.updateMPYear(this.mpyear-10);
26447         }
26448         else if(el.is('a.x-date-mp-next')){
26449             this.updateMPYear(this.mpyear+10);
26450         }
26451     },
26452
26453     onMonthDblClick : function(e, t){
26454         e.stopEvent();
26455         var el = new Roo.Element(t), pn;
26456         if(pn = el.up('td.x-date-mp-month', 2)){
26457             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26458             this.hideMonthPicker();
26459         }
26460         else if(pn = el.up('td.x-date-mp-year', 2)){
26461             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26462             this.hideMonthPicker();
26463         }
26464     },
26465
26466     hideMonthPicker : function(disableAnim){
26467         if(this.monthPicker){
26468             if(disableAnim === true){
26469                 this.monthPicker.hide();
26470             }else{
26471                 this.monthPicker.slideOut('t', {duration:.2});
26472             }
26473         }
26474     },
26475
26476     // private
26477     showPrevMonth : function(e){
26478         this.update(this.activeDate.add("mo", -1));
26479     },
26480
26481     // private
26482     showNextMonth : function(e){
26483         this.update(this.activeDate.add("mo", 1));
26484     },
26485
26486     // private
26487     showPrevYear : function(){
26488         this.update(this.activeDate.add("y", -1));
26489     },
26490
26491     // private
26492     showNextYear : function(){
26493         this.update(this.activeDate.add("y", 1));
26494     },
26495
26496     // private
26497     handleMouseWheel : function(e){
26498         var delta = e.getWheelDelta();
26499         if(delta > 0){
26500             this.showPrevMonth();
26501             e.stopEvent();
26502         } else if(delta < 0){
26503             this.showNextMonth();
26504             e.stopEvent();
26505         }
26506     },
26507
26508     // private
26509     handleDateClick : function(e, t){
26510         e.stopEvent();
26511         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26512             this.setValue(new Date(t.dateValue));
26513             this.fireEvent("select", this, this.value);
26514         }
26515     },
26516
26517     // private
26518     selectToday : function(){
26519         this.setValue(new Date().clearTime());
26520         this.fireEvent("select", this, this.value);
26521     },
26522
26523     // private
26524     update : function(date)
26525     {
26526         var vd = this.activeDate;
26527         this.activeDate = date;
26528         if(vd && this.el){
26529             var t = date.getTime();
26530             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26531                 this.cells.removeClass("x-date-selected");
26532                 this.cells.each(function(c){
26533                    if(c.dom.firstChild.dateValue == t){
26534                        c.addClass("x-date-selected");
26535                        setTimeout(function(){
26536                             try{c.dom.firstChild.focus();}catch(e){}
26537                        }, 50);
26538                        return false;
26539                    }
26540                 });
26541                 return;
26542             }
26543         }
26544         
26545         var days = date.getDaysInMonth();
26546         var firstOfMonth = date.getFirstDateOfMonth();
26547         var startingPos = firstOfMonth.getDay()-this.startDay;
26548
26549         if(startingPos <= this.startDay){
26550             startingPos += 7;
26551         }
26552
26553         var pm = date.add("mo", -1);
26554         var prevStart = pm.getDaysInMonth()-startingPos;
26555
26556         var cells = this.cells.elements;
26557         var textEls = this.textNodes;
26558         days += startingPos;
26559
26560         // convert everything to numbers so it's fast
26561         var day = 86400000;
26562         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26563         var today = new Date().clearTime().getTime();
26564         var sel = date.clearTime().getTime();
26565         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26566         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26567         var ddMatch = this.disabledDatesRE;
26568         var ddText = this.disabledDatesText;
26569         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26570         var ddaysText = this.disabledDaysText;
26571         var format = this.format;
26572
26573         var setCellClass = function(cal, cell){
26574             cell.title = "";
26575             var t = d.getTime();
26576             cell.firstChild.dateValue = t;
26577             if(t == today){
26578                 cell.className += " x-date-today";
26579                 cell.title = cal.todayText;
26580             }
26581             if(t == sel){
26582                 cell.className += " x-date-selected";
26583                 setTimeout(function(){
26584                     try{cell.firstChild.focus();}catch(e){}
26585                 }, 50);
26586             }
26587             // disabling
26588             if(t < min) {
26589                 cell.className = " x-date-disabled";
26590                 cell.title = cal.minText;
26591                 return;
26592             }
26593             if(t > max) {
26594                 cell.className = " x-date-disabled";
26595                 cell.title = cal.maxText;
26596                 return;
26597             }
26598             if(ddays){
26599                 if(ddays.indexOf(d.getDay()) != -1){
26600                     cell.title = ddaysText;
26601                     cell.className = " x-date-disabled";
26602                 }
26603             }
26604             if(ddMatch && format){
26605                 var fvalue = d.dateFormat(format);
26606                 if(ddMatch.test(fvalue)){
26607                     cell.title = ddText.replace("%0", fvalue);
26608                     cell.className = " x-date-disabled";
26609                 }
26610             }
26611         };
26612
26613         var i = 0;
26614         for(; i < startingPos; i++) {
26615             textEls[i].innerHTML = (++prevStart);
26616             d.setDate(d.getDate()+1);
26617             cells[i].className = "x-date-prevday";
26618             setCellClass(this, cells[i]);
26619         }
26620         for(; i < days; i++){
26621             intDay = i - startingPos + 1;
26622             textEls[i].innerHTML = (intDay);
26623             d.setDate(d.getDate()+1);
26624             cells[i].className = "x-date-active";
26625             setCellClass(this, cells[i]);
26626         }
26627         var extraDays = 0;
26628         for(; i < 42; i++) {
26629              textEls[i].innerHTML = (++extraDays);
26630              d.setDate(d.getDate()+1);
26631              cells[i].className = "x-date-nextday";
26632              setCellClass(this, cells[i]);
26633         }
26634
26635         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26636         this.fireEvent('monthchange', this, date);
26637         
26638         if(!this.internalRender){
26639             var main = this.el.dom.firstChild;
26640             var w = main.offsetWidth;
26641             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26642             Roo.fly(main).setWidth(w);
26643             this.internalRender = true;
26644             // opera does not respect the auto grow header center column
26645             // then, after it gets a width opera refuses to recalculate
26646             // without a second pass
26647             if(Roo.isOpera && !this.secondPass){
26648                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26649                 this.secondPass = true;
26650                 this.update.defer(10, this, [date]);
26651             }
26652         }
26653         
26654         
26655     }
26656 });        /*
26657  * Based on:
26658  * Ext JS Library 1.1.1
26659  * Copyright(c) 2006-2007, Ext JS, LLC.
26660  *
26661  * Originally Released Under LGPL - original licence link has changed is not relivant.
26662  *
26663  * Fork - LGPL
26664  * <script type="text/javascript">
26665  */
26666 /**
26667  * @class Roo.TabPanel
26668  * @extends Roo.util.Observable
26669  * A lightweight tab container.
26670  * <br><br>
26671  * Usage:
26672  * <pre><code>
26673 // basic tabs 1, built from existing content
26674 var tabs = new Roo.TabPanel("tabs1");
26675 tabs.addTab("script", "View Script");
26676 tabs.addTab("markup", "View Markup");
26677 tabs.activate("script");
26678
26679 // more advanced tabs, built from javascript
26680 var jtabs = new Roo.TabPanel("jtabs");
26681 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26682
26683 // set up the UpdateManager
26684 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26685 var updater = tab2.getUpdateManager();
26686 updater.setDefaultUrl("ajax1.htm");
26687 tab2.on('activate', updater.refresh, updater, true);
26688
26689 // Use setUrl for Ajax loading
26690 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26691 tab3.setUrl("ajax2.htm", null, true);
26692
26693 // Disabled tab
26694 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26695 tab4.disable();
26696
26697 jtabs.activate("jtabs-1");
26698  * </code></pre>
26699  * @constructor
26700  * Create a new TabPanel.
26701  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26702  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26703  */
26704 Roo.TabPanel = function(container, config){
26705     /**
26706     * The container element for this TabPanel.
26707     * @type Roo.Element
26708     */
26709     this.el = Roo.get(container, true);
26710     if(config){
26711         if(typeof config == "boolean"){
26712             this.tabPosition = config ? "bottom" : "top";
26713         }else{
26714             Roo.apply(this, config);
26715         }
26716     }
26717     if(this.tabPosition == "bottom"){
26718         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26719         this.el.addClass("x-tabs-bottom");
26720     }
26721     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26722     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26723     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26724     if(Roo.isIE){
26725         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26726     }
26727     if(this.tabPosition != "bottom"){
26728         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26729          * @type Roo.Element
26730          */
26731         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26732         this.el.addClass("x-tabs-top");
26733     }
26734     this.items = [];
26735
26736     this.bodyEl.setStyle("position", "relative");
26737
26738     this.active = null;
26739     this.activateDelegate = this.activate.createDelegate(this);
26740
26741     this.addEvents({
26742         /**
26743          * @event tabchange
26744          * Fires when the active tab changes
26745          * @param {Roo.TabPanel} this
26746          * @param {Roo.TabPanelItem} activePanel The new active tab
26747          */
26748         "tabchange": true,
26749         /**
26750          * @event beforetabchange
26751          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26752          * @param {Roo.TabPanel} this
26753          * @param {Object} e Set cancel to true on this object to cancel the tab change
26754          * @param {Roo.TabPanelItem} tab The tab being changed to
26755          */
26756         "beforetabchange" : true
26757     });
26758
26759     Roo.EventManager.onWindowResize(this.onResize, this);
26760     this.cpad = this.el.getPadding("lr");
26761     this.hiddenCount = 0;
26762
26763
26764     // toolbar on the tabbar support...
26765     if (this.toolbar) {
26766         var tcfg = this.toolbar;
26767         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26768         this.toolbar = new Roo.Toolbar(tcfg);
26769         if (Roo.isSafari) {
26770             var tbl = tcfg.container.child('table', true);
26771             tbl.setAttribute('width', '100%');
26772         }
26773         
26774     }
26775    
26776
26777
26778     Roo.TabPanel.superclass.constructor.call(this);
26779 };
26780
26781 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26782     /*
26783      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26784      */
26785     tabPosition : "top",
26786     /*
26787      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26788      */
26789     currentTabWidth : 0,
26790     /*
26791      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26792      */
26793     minTabWidth : 40,
26794     /*
26795      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26796      */
26797     maxTabWidth : 250,
26798     /*
26799      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26800      */
26801     preferredTabWidth : 175,
26802     /*
26803      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26804      */
26805     resizeTabs : false,
26806     /*
26807      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26808      */
26809     monitorResize : true,
26810     /*
26811      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26812      */
26813     toolbar : false,
26814
26815     /**
26816      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26817      * @param {String} id The id of the div to use <b>or create</b>
26818      * @param {String} text The text for the tab
26819      * @param {String} content (optional) Content to put in the TabPanelItem body
26820      * @param {Boolean} closable (optional) True to create a close icon on the tab
26821      * @return {Roo.TabPanelItem} The created TabPanelItem
26822      */
26823     addTab : function(id, text, content, closable){
26824         var item = new Roo.TabPanelItem(this, id, text, closable);
26825         this.addTabItem(item);
26826         if(content){
26827             item.setContent(content);
26828         }
26829         return item;
26830     },
26831
26832     /**
26833      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26834      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26835      * @return {Roo.TabPanelItem}
26836      */
26837     getTab : function(id){
26838         return this.items[id];
26839     },
26840
26841     /**
26842      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26843      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26844      */
26845     hideTab : function(id){
26846         var t = this.items[id];
26847         if(!t.isHidden()){
26848            t.setHidden(true);
26849            this.hiddenCount++;
26850            this.autoSizeTabs();
26851         }
26852     },
26853
26854     /**
26855      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26856      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26857      */
26858     unhideTab : function(id){
26859         var t = this.items[id];
26860         if(t.isHidden()){
26861            t.setHidden(false);
26862            this.hiddenCount--;
26863            this.autoSizeTabs();
26864         }
26865     },
26866
26867     /**
26868      * Adds an existing {@link Roo.TabPanelItem}.
26869      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26870      */
26871     addTabItem : function(item){
26872         this.items[item.id] = item;
26873         this.items.push(item);
26874         if(this.resizeTabs){
26875            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26876            this.autoSizeTabs();
26877         }else{
26878             item.autoSize();
26879         }
26880     },
26881
26882     /**
26883      * Removes a {@link Roo.TabPanelItem}.
26884      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26885      */
26886     removeTab : function(id){
26887         var items = this.items;
26888         var tab = items[id];
26889         if(!tab) { return; }
26890         var index = items.indexOf(tab);
26891         if(this.active == tab && items.length > 1){
26892             var newTab = this.getNextAvailable(index);
26893             if(newTab) {
26894                 newTab.activate();
26895             }
26896         }
26897         this.stripEl.dom.removeChild(tab.pnode.dom);
26898         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26899             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26900         }
26901         items.splice(index, 1);
26902         delete this.items[tab.id];
26903         tab.fireEvent("close", tab);
26904         tab.purgeListeners();
26905         this.autoSizeTabs();
26906     },
26907
26908     getNextAvailable : function(start){
26909         var items = this.items;
26910         var index = start;
26911         // look for a next tab that will slide over to
26912         // replace the one being removed
26913         while(index < items.length){
26914             var item = items[++index];
26915             if(item && !item.isHidden()){
26916                 return item;
26917             }
26918         }
26919         // if one isn't found select the previous tab (on the left)
26920         index = start;
26921         while(index >= 0){
26922             var item = items[--index];
26923             if(item && !item.isHidden()){
26924                 return item;
26925             }
26926         }
26927         return null;
26928     },
26929
26930     /**
26931      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26932      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26933      */
26934     disableTab : function(id){
26935         var tab = this.items[id];
26936         if(tab && this.active != tab){
26937             tab.disable();
26938         }
26939     },
26940
26941     /**
26942      * Enables a {@link Roo.TabPanelItem} that is disabled.
26943      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26944      */
26945     enableTab : function(id){
26946         var tab = this.items[id];
26947         tab.enable();
26948     },
26949
26950     /**
26951      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26952      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26953      * @return {Roo.TabPanelItem} The TabPanelItem.
26954      */
26955     activate : function(id){
26956         var tab = this.items[id];
26957         if(!tab){
26958             return null;
26959         }
26960         if(tab == this.active || tab.disabled){
26961             return tab;
26962         }
26963         var e = {};
26964         this.fireEvent("beforetabchange", this, e, tab);
26965         if(e.cancel !== true && !tab.disabled){
26966             if(this.active){
26967                 this.active.hide();
26968             }
26969             this.active = this.items[id];
26970             this.active.show();
26971             this.fireEvent("tabchange", this, this.active);
26972         }
26973         return tab;
26974     },
26975
26976     /**
26977      * Gets the active {@link Roo.TabPanelItem}.
26978      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26979      */
26980     getActiveTab : function(){
26981         return this.active;
26982     },
26983
26984     /**
26985      * Updates the tab body element to fit the height of the container element
26986      * for overflow scrolling
26987      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26988      */
26989     syncHeight : function(targetHeight){
26990         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26991         var bm = this.bodyEl.getMargins();
26992         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26993         this.bodyEl.setHeight(newHeight);
26994         return newHeight;
26995     },
26996
26997     onResize : function(){
26998         if(this.monitorResize){
26999             this.autoSizeTabs();
27000         }
27001     },
27002
27003     /**
27004      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
27005      */
27006     beginUpdate : function(){
27007         this.updating = true;
27008     },
27009
27010     /**
27011      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27012      */
27013     endUpdate : function(){
27014         this.updating = false;
27015         this.autoSizeTabs();
27016     },
27017
27018     /**
27019      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27020      */
27021     autoSizeTabs : function(){
27022         var count = this.items.length;
27023         var vcount = count - this.hiddenCount;
27024         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27025         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27026         var availWidth = Math.floor(w / vcount);
27027         var b = this.stripBody;
27028         if(b.getWidth() > w){
27029             var tabs = this.items;
27030             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27031             if(availWidth < this.minTabWidth){
27032                 /*if(!this.sleft){    // incomplete scrolling code
27033                     this.createScrollButtons();
27034                 }
27035                 this.showScroll();
27036                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27037             }
27038         }else{
27039             if(this.currentTabWidth < this.preferredTabWidth){
27040                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27041             }
27042         }
27043     },
27044
27045     /**
27046      * Returns the number of tabs in this TabPanel.
27047      * @return {Number}
27048      */
27049      getCount : function(){
27050          return this.items.length;
27051      },
27052
27053     /**
27054      * Resizes all the tabs to the passed width
27055      * @param {Number} The new width
27056      */
27057     setTabWidth : function(width){
27058         this.currentTabWidth = width;
27059         for(var i = 0, len = this.items.length; i < len; i++) {
27060                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27061         }
27062     },
27063
27064     /**
27065      * Destroys this TabPanel
27066      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27067      */
27068     destroy : function(removeEl){
27069         Roo.EventManager.removeResizeListener(this.onResize, this);
27070         for(var i = 0, len = this.items.length; i < len; i++){
27071             this.items[i].purgeListeners();
27072         }
27073         if(removeEl === true){
27074             this.el.update("");
27075             this.el.remove();
27076         }
27077     }
27078 });
27079
27080 /**
27081  * @class Roo.TabPanelItem
27082  * @extends Roo.util.Observable
27083  * Represents an individual item (tab plus body) in a TabPanel.
27084  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27085  * @param {String} id The id of this TabPanelItem
27086  * @param {String} text The text for the tab of this TabPanelItem
27087  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27088  */
27089 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27090     /**
27091      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27092      * @type Roo.TabPanel
27093      */
27094     this.tabPanel = tabPanel;
27095     /**
27096      * The id for this TabPanelItem
27097      * @type String
27098      */
27099     this.id = id;
27100     /** @private */
27101     this.disabled = false;
27102     /** @private */
27103     this.text = text;
27104     /** @private */
27105     this.loaded = false;
27106     this.closable = closable;
27107
27108     /**
27109      * The body element for this TabPanelItem.
27110      * @type Roo.Element
27111      */
27112     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27113     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27114     this.bodyEl.setStyle("display", "block");
27115     this.bodyEl.setStyle("zoom", "1");
27116     this.hideAction();
27117
27118     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27119     /** @private */
27120     this.el = Roo.get(els.el, true);
27121     this.inner = Roo.get(els.inner, true);
27122     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27123     this.pnode = Roo.get(els.el.parentNode, true);
27124     this.el.on("mousedown", this.onTabMouseDown, this);
27125     this.el.on("click", this.onTabClick, this);
27126     /** @private */
27127     if(closable){
27128         var c = Roo.get(els.close, true);
27129         c.dom.title = this.closeText;
27130         c.addClassOnOver("close-over");
27131         c.on("click", this.closeClick, this);
27132      }
27133
27134     this.addEvents({
27135          /**
27136          * @event activate
27137          * Fires when this tab becomes the active tab.
27138          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27139          * @param {Roo.TabPanelItem} this
27140          */
27141         "activate": true,
27142         /**
27143          * @event beforeclose
27144          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27145          * @param {Roo.TabPanelItem} this
27146          * @param {Object} e Set cancel to true on this object to cancel the close.
27147          */
27148         "beforeclose": true,
27149         /**
27150          * @event close
27151          * Fires when this tab is closed.
27152          * @param {Roo.TabPanelItem} this
27153          */
27154          "close": true,
27155         /**
27156          * @event deactivate
27157          * Fires when this tab is no longer the active tab.
27158          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27159          * @param {Roo.TabPanelItem} this
27160          */
27161          "deactivate" : true
27162     });
27163     this.hidden = false;
27164
27165     Roo.TabPanelItem.superclass.constructor.call(this);
27166 };
27167
27168 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27169     purgeListeners : function(){
27170        Roo.util.Observable.prototype.purgeListeners.call(this);
27171        this.el.removeAllListeners();
27172     },
27173     /**
27174      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27175      */
27176     show : function(){
27177         this.pnode.addClass("on");
27178         this.showAction();
27179         if(Roo.isOpera){
27180             this.tabPanel.stripWrap.repaint();
27181         }
27182         this.fireEvent("activate", this.tabPanel, this);
27183     },
27184
27185     /**
27186      * Returns true if this tab is the active tab.
27187      * @return {Boolean}
27188      */
27189     isActive : function(){
27190         return this.tabPanel.getActiveTab() == this;
27191     },
27192
27193     /**
27194      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27195      */
27196     hide : function(){
27197         this.pnode.removeClass("on");
27198         this.hideAction();
27199         this.fireEvent("deactivate", this.tabPanel, this);
27200     },
27201
27202     hideAction : function(){
27203         this.bodyEl.hide();
27204         this.bodyEl.setStyle("position", "absolute");
27205         this.bodyEl.setLeft("-20000px");
27206         this.bodyEl.setTop("-20000px");
27207     },
27208
27209     showAction : function(){
27210         this.bodyEl.setStyle("position", "relative");
27211         this.bodyEl.setTop("");
27212         this.bodyEl.setLeft("");
27213         this.bodyEl.show();
27214     },
27215
27216     /**
27217      * Set the tooltip for the tab.
27218      * @param {String} tooltip The tab's tooltip
27219      */
27220     setTooltip : function(text){
27221         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27222             this.textEl.dom.qtip = text;
27223             this.textEl.dom.removeAttribute('title');
27224         }else{
27225             this.textEl.dom.title = text;
27226         }
27227     },
27228
27229     onTabClick : function(e){
27230         e.preventDefault();
27231         this.tabPanel.activate(this.id);
27232     },
27233
27234     onTabMouseDown : function(e){
27235         e.preventDefault();
27236         this.tabPanel.activate(this.id);
27237     },
27238
27239     getWidth : function(){
27240         return this.inner.getWidth();
27241     },
27242
27243     setWidth : function(width){
27244         var iwidth = width - this.pnode.getPadding("lr");
27245         this.inner.setWidth(iwidth);
27246         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27247         this.pnode.setWidth(width);
27248     },
27249
27250     /**
27251      * Show or hide the tab
27252      * @param {Boolean} hidden True to hide or false to show.
27253      */
27254     setHidden : function(hidden){
27255         this.hidden = hidden;
27256         this.pnode.setStyle("display", hidden ? "none" : "");
27257     },
27258
27259     /**
27260      * Returns true if this tab is "hidden"
27261      * @return {Boolean}
27262      */
27263     isHidden : function(){
27264         return this.hidden;
27265     },
27266
27267     /**
27268      * Returns the text for this tab
27269      * @return {String}
27270      */
27271     getText : function(){
27272         return this.text;
27273     },
27274
27275     autoSize : function(){
27276         //this.el.beginMeasure();
27277         this.textEl.setWidth(1);
27278         /*
27279          *  #2804 [new] Tabs in Roojs
27280          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27281          */
27282         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27283         //this.el.endMeasure();
27284     },
27285
27286     /**
27287      * Sets the text for the tab (Note: this also sets the tooltip text)
27288      * @param {String} text The tab's text and tooltip
27289      */
27290     setText : function(text){
27291         this.text = text;
27292         this.textEl.update(text);
27293         this.setTooltip(text);
27294         if(!this.tabPanel.resizeTabs){
27295             this.autoSize();
27296         }
27297     },
27298     /**
27299      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27300      */
27301     activate : function(){
27302         this.tabPanel.activate(this.id);
27303     },
27304
27305     /**
27306      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27307      */
27308     disable : function(){
27309         if(this.tabPanel.active != this){
27310             this.disabled = true;
27311             this.pnode.addClass("disabled");
27312         }
27313     },
27314
27315     /**
27316      * Enables this TabPanelItem if it was previously disabled.
27317      */
27318     enable : function(){
27319         this.disabled = false;
27320         this.pnode.removeClass("disabled");
27321     },
27322
27323     /**
27324      * Sets the content for this TabPanelItem.
27325      * @param {String} content The content
27326      * @param {Boolean} loadScripts true to look for and load scripts
27327      */
27328     setContent : function(content, loadScripts){
27329         this.bodyEl.update(content, loadScripts);
27330     },
27331
27332     /**
27333      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27334      * @return {Roo.UpdateManager} The UpdateManager
27335      */
27336     getUpdateManager : function(){
27337         return this.bodyEl.getUpdateManager();
27338     },
27339
27340     /**
27341      * Set a URL to be used to load the content for this TabPanelItem.
27342      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27343      * @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)
27344      * @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)
27345      * @return {Roo.UpdateManager} The UpdateManager
27346      */
27347     setUrl : function(url, params, loadOnce){
27348         if(this.refreshDelegate){
27349             this.un('activate', this.refreshDelegate);
27350         }
27351         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27352         this.on("activate", this.refreshDelegate);
27353         return this.bodyEl.getUpdateManager();
27354     },
27355
27356     /** @private */
27357     _handleRefresh : function(url, params, loadOnce){
27358         if(!loadOnce || !this.loaded){
27359             var updater = this.bodyEl.getUpdateManager();
27360             updater.update(url, params, this._setLoaded.createDelegate(this));
27361         }
27362     },
27363
27364     /**
27365      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27366      *   Will fail silently if the setUrl method has not been called.
27367      *   This does not activate the panel, just updates its content.
27368      */
27369     refresh : function(){
27370         if(this.refreshDelegate){
27371            this.loaded = false;
27372            this.refreshDelegate();
27373         }
27374     },
27375
27376     /** @private */
27377     _setLoaded : function(){
27378         this.loaded = true;
27379     },
27380
27381     /** @private */
27382     closeClick : function(e){
27383         var o = {};
27384         e.stopEvent();
27385         this.fireEvent("beforeclose", this, o);
27386         if(o.cancel !== true){
27387             this.tabPanel.removeTab(this.id);
27388         }
27389     },
27390     /**
27391      * The text displayed in the tooltip for the close icon.
27392      * @type String
27393      */
27394     closeText : "Close this tab"
27395 });
27396
27397 /** @private */
27398 Roo.TabPanel.prototype.createStrip = function(container){
27399     var strip = document.createElement("div");
27400     strip.className = "x-tabs-wrap";
27401     container.appendChild(strip);
27402     return strip;
27403 };
27404 /** @private */
27405 Roo.TabPanel.prototype.createStripList = function(strip){
27406     // div wrapper for retard IE
27407     // returns the "tr" element.
27408     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27409         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27410         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27411     return strip.firstChild.firstChild.firstChild.firstChild;
27412 };
27413 /** @private */
27414 Roo.TabPanel.prototype.createBody = function(container){
27415     var body = document.createElement("div");
27416     Roo.id(body, "tab-body");
27417     Roo.fly(body).addClass("x-tabs-body");
27418     container.appendChild(body);
27419     return body;
27420 };
27421 /** @private */
27422 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27423     var body = Roo.getDom(id);
27424     if(!body){
27425         body = document.createElement("div");
27426         body.id = id;
27427     }
27428     Roo.fly(body).addClass("x-tabs-item-body");
27429     bodyEl.insertBefore(body, bodyEl.firstChild);
27430     return body;
27431 };
27432 /** @private */
27433 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27434     var td = document.createElement("td");
27435     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27436     //stripEl.appendChild(td);
27437     if(closable){
27438         td.className = "x-tabs-closable";
27439         if(!this.closeTpl){
27440             this.closeTpl = new Roo.Template(
27441                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27442                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27443                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27444             );
27445         }
27446         var el = this.closeTpl.overwrite(td, {"text": text});
27447         var close = el.getElementsByTagName("div")[0];
27448         var inner = el.getElementsByTagName("em")[0];
27449         return {"el": el, "close": close, "inner": inner};
27450     } else {
27451         if(!this.tabTpl){
27452             this.tabTpl = new Roo.Template(
27453                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27454                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27455             );
27456         }
27457         var el = this.tabTpl.overwrite(td, {"text": text});
27458         var inner = el.getElementsByTagName("em")[0];
27459         return {"el": el, "inner": inner};
27460     }
27461 };/*
27462  * Based on:
27463  * Ext JS Library 1.1.1
27464  * Copyright(c) 2006-2007, Ext JS, LLC.
27465  *
27466  * Originally Released Under LGPL - original licence link has changed is not relivant.
27467  *
27468  * Fork - LGPL
27469  * <script type="text/javascript">
27470  */
27471
27472 /**
27473  * @class Roo.Button
27474  * @extends Roo.util.Observable
27475  * Simple Button class
27476  * @cfg {String} text The button text
27477  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27478  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27479  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27480  * @cfg {Object} scope The scope of the handler
27481  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27482  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27483  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27484  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27485  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27486  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27487    applies if enableToggle = true)
27488  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27489  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27490   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27491  * @constructor
27492  * Create a new button
27493  * @param {Object} config The config object
27494  */
27495 Roo.Button = function(renderTo, config)
27496 {
27497     if (!config) {
27498         config = renderTo;
27499         renderTo = config.renderTo || false;
27500     }
27501     
27502     Roo.apply(this, config);
27503     this.addEvents({
27504         /**
27505              * @event click
27506              * Fires when this button is clicked
27507              * @param {Button} this
27508              * @param {EventObject} e The click event
27509              */
27510             "click" : true,
27511         /**
27512              * @event toggle
27513              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27514              * @param {Button} this
27515              * @param {Boolean} pressed
27516              */
27517             "toggle" : true,
27518         /**
27519              * @event mouseover
27520              * Fires when the mouse hovers over the button
27521              * @param {Button} this
27522              * @param {Event} e The event object
27523              */
27524         'mouseover' : true,
27525         /**
27526              * @event mouseout
27527              * Fires when the mouse exits the button
27528              * @param {Button} this
27529              * @param {Event} e The event object
27530              */
27531         'mouseout': true,
27532          /**
27533              * @event render
27534              * Fires when the button is rendered
27535              * @param {Button} this
27536              */
27537         'render': true
27538     });
27539     if(this.menu){
27540         this.menu = Roo.menu.MenuMgr.get(this.menu);
27541     }
27542     // register listeners first!!  - so render can be captured..
27543     Roo.util.Observable.call(this);
27544     if(renderTo){
27545         this.render(renderTo);
27546     }
27547     
27548   
27549 };
27550
27551 Roo.extend(Roo.Button, Roo.util.Observable, {
27552     /**
27553      * 
27554      */
27555     
27556     /**
27557      * Read-only. True if this button is hidden
27558      * @type Boolean
27559      */
27560     hidden : false,
27561     /**
27562      * Read-only. True if this button is disabled
27563      * @type Boolean
27564      */
27565     disabled : false,
27566     /**
27567      * Read-only. True if this button is pressed (only if enableToggle = true)
27568      * @type Boolean
27569      */
27570     pressed : false,
27571
27572     /**
27573      * @cfg {Number} tabIndex 
27574      * The DOM tabIndex for this button (defaults to undefined)
27575      */
27576     tabIndex : undefined,
27577
27578     /**
27579      * @cfg {Boolean} enableToggle
27580      * True to enable pressed/not pressed toggling (defaults to false)
27581      */
27582     enableToggle: false,
27583     /**
27584      * @cfg {Mixed} menu
27585      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27586      */
27587     menu : undefined,
27588     /**
27589      * @cfg {String} menuAlign
27590      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27591      */
27592     menuAlign : "tl-bl?",
27593
27594     /**
27595      * @cfg {String} iconCls
27596      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27597      */
27598     iconCls : undefined,
27599     /**
27600      * @cfg {String} type
27601      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27602      */
27603     type : 'button',
27604
27605     // private
27606     menuClassTarget: 'tr',
27607
27608     /**
27609      * @cfg {String} clickEvent
27610      * The type of event to map to the button's event handler (defaults to 'click')
27611      */
27612     clickEvent : 'click',
27613
27614     /**
27615      * @cfg {Boolean} handleMouseEvents
27616      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27617      */
27618     handleMouseEvents : true,
27619
27620     /**
27621      * @cfg {String} tooltipType
27622      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27623      */
27624     tooltipType : 'qtip',
27625
27626     /**
27627      * @cfg {String} cls
27628      * A CSS class to apply to the button's main element.
27629      */
27630     
27631     /**
27632      * @cfg {Roo.Template} template (Optional)
27633      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27634      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27635      * require code modifications if required elements (e.g. a button) aren't present.
27636      */
27637
27638     // private
27639     render : function(renderTo){
27640         var btn;
27641         if(this.hideParent){
27642             this.parentEl = Roo.get(renderTo);
27643         }
27644         if(!this.dhconfig){
27645             if(!this.template){
27646                 if(!Roo.Button.buttonTemplate){
27647                     // hideous table template
27648                     Roo.Button.buttonTemplate = new Roo.Template(
27649                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27650                         '<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>',
27651                         "</tr></tbody></table>");
27652                 }
27653                 this.template = Roo.Button.buttonTemplate;
27654             }
27655             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27656             var btnEl = btn.child("button:first");
27657             btnEl.on('focus', this.onFocus, this);
27658             btnEl.on('blur', this.onBlur, this);
27659             if(this.cls){
27660                 btn.addClass(this.cls);
27661             }
27662             if(this.icon){
27663                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27664             }
27665             if(this.iconCls){
27666                 btnEl.addClass(this.iconCls);
27667                 if(!this.cls){
27668                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27669                 }
27670             }
27671             if(this.tabIndex !== undefined){
27672                 btnEl.dom.tabIndex = this.tabIndex;
27673             }
27674             if(this.tooltip){
27675                 if(typeof this.tooltip == 'object'){
27676                     Roo.QuickTips.tips(Roo.apply({
27677                           target: btnEl.id
27678                     }, this.tooltip));
27679                 } else {
27680                     btnEl.dom[this.tooltipType] = this.tooltip;
27681                 }
27682             }
27683         }else{
27684             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27685         }
27686         this.el = btn;
27687         if(this.id){
27688             this.el.dom.id = this.el.id = this.id;
27689         }
27690         if(this.menu){
27691             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27692             this.menu.on("show", this.onMenuShow, this);
27693             this.menu.on("hide", this.onMenuHide, this);
27694         }
27695         btn.addClass("x-btn");
27696         if(Roo.isIE && !Roo.isIE7){
27697             this.autoWidth.defer(1, this);
27698         }else{
27699             this.autoWidth();
27700         }
27701         if(this.handleMouseEvents){
27702             btn.on("mouseover", this.onMouseOver, this);
27703             btn.on("mouseout", this.onMouseOut, this);
27704             btn.on("mousedown", this.onMouseDown, this);
27705         }
27706         btn.on(this.clickEvent, this.onClick, this);
27707         //btn.on("mouseup", this.onMouseUp, this);
27708         if(this.hidden){
27709             this.hide();
27710         }
27711         if(this.disabled){
27712             this.disable();
27713         }
27714         Roo.ButtonToggleMgr.register(this);
27715         if(this.pressed){
27716             this.el.addClass("x-btn-pressed");
27717         }
27718         if(this.repeat){
27719             var repeater = new Roo.util.ClickRepeater(btn,
27720                 typeof this.repeat == "object" ? this.repeat : {}
27721             );
27722             repeater.on("click", this.onClick,  this);
27723         }
27724         
27725         this.fireEvent('render', this);
27726         
27727     },
27728     /**
27729      * Returns the button's underlying element
27730      * @return {Roo.Element} The element
27731      */
27732     getEl : function(){
27733         return this.el;  
27734     },
27735     
27736     /**
27737      * Destroys this Button and removes any listeners.
27738      */
27739     destroy : function(){
27740         Roo.ButtonToggleMgr.unregister(this);
27741         this.el.removeAllListeners();
27742         this.purgeListeners();
27743         this.el.remove();
27744     },
27745
27746     // private
27747     autoWidth : function(){
27748         if(this.el){
27749             this.el.setWidth("auto");
27750             if(Roo.isIE7 && Roo.isStrict){
27751                 var ib = this.el.child('button');
27752                 if(ib && ib.getWidth() > 20){
27753                     ib.clip();
27754                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27755                 }
27756             }
27757             if(this.minWidth){
27758                 if(this.hidden){
27759                     this.el.beginMeasure();
27760                 }
27761                 if(this.el.getWidth() < this.minWidth){
27762                     this.el.setWidth(this.minWidth);
27763                 }
27764                 if(this.hidden){
27765                     this.el.endMeasure();
27766                 }
27767             }
27768         }
27769     },
27770
27771     /**
27772      * Assigns this button's click handler
27773      * @param {Function} handler The function to call when the button is clicked
27774      * @param {Object} scope (optional) Scope for the function passed in
27775      */
27776     setHandler : function(handler, scope){
27777         this.handler = handler;
27778         this.scope = scope;  
27779     },
27780     
27781     /**
27782      * Sets this button's text
27783      * @param {String} text The button text
27784      */
27785     setText : function(text){
27786         this.text = text;
27787         if(this.el){
27788             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27789         }
27790         this.autoWidth();
27791     },
27792     
27793     /**
27794      * Gets the text for this button
27795      * @return {String} The button text
27796      */
27797     getText : function(){
27798         return this.text;  
27799     },
27800     
27801     /**
27802      * Show this button
27803      */
27804     show: function(){
27805         this.hidden = false;
27806         if(this.el){
27807             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27808         }
27809     },
27810     
27811     /**
27812      * Hide this button
27813      */
27814     hide: function(){
27815         this.hidden = true;
27816         if(this.el){
27817             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27818         }
27819     },
27820     
27821     /**
27822      * Convenience function for boolean show/hide
27823      * @param {Boolean} visible True to show, false to hide
27824      */
27825     setVisible: function(visible){
27826         if(visible) {
27827             this.show();
27828         }else{
27829             this.hide();
27830         }
27831     },
27832     
27833     /**
27834      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27835      * @param {Boolean} state (optional) Force a particular state
27836      */
27837     toggle : function(state){
27838         state = state === undefined ? !this.pressed : state;
27839         if(state != this.pressed){
27840             if(state){
27841                 this.el.addClass("x-btn-pressed");
27842                 this.pressed = true;
27843                 this.fireEvent("toggle", this, true);
27844             }else{
27845                 this.el.removeClass("x-btn-pressed");
27846                 this.pressed = false;
27847                 this.fireEvent("toggle", this, false);
27848             }
27849             if(this.toggleHandler){
27850                 this.toggleHandler.call(this.scope || this, this, state);
27851             }
27852         }
27853     },
27854     
27855     /**
27856      * Focus the button
27857      */
27858     focus : function(){
27859         this.el.child('button:first').focus();
27860     },
27861     
27862     /**
27863      * Disable this button
27864      */
27865     disable : function(){
27866         if(this.el){
27867             this.el.addClass("x-btn-disabled");
27868         }
27869         this.disabled = true;
27870     },
27871     
27872     /**
27873      * Enable this button
27874      */
27875     enable : function(){
27876         if(this.el){
27877             this.el.removeClass("x-btn-disabled");
27878         }
27879         this.disabled = false;
27880     },
27881
27882     /**
27883      * Convenience function for boolean enable/disable
27884      * @param {Boolean} enabled True to enable, false to disable
27885      */
27886     setDisabled : function(v){
27887         this[v !== true ? "enable" : "disable"]();
27888     },
27889
27890     // private
27891     onClick : function(e)
27892     {
27893         if(e){
27894             e.preventDefault();
27895         }
27896         if(e.button != 0){
27897             return;
27898         }
27899         if(!this.disabled){
27900             if(this.enableToggle){
27901                 this.toggle();
27902             }
27903             if(this.menu && !this.menu.isVisible()){
27904                 this.menu.show(this.el, this.menuAlign);
27905             }
27906             this.fireEvent("click", this, e);
27907             if(this.handler){
27908                 this.el.removeClass("x-btn-over");
27909                 this.handler.call(this.scope || this, this, e);
27910             }
27911         }
27912     },
27913     // private
27914     onMouseOver : function(e){
27915         if(!this.disabled){
27916             this.el.addClass("x-btn-over");
27917             this.fireEvent('mouseover', this, e);
27918         }
27919     },
27920     // private
27921     onMouseOut : function(e){
27922         if(!e.within(this.el,  true)){
27923             this.el.removeClass("x-btn-over");
27924             this.fireEvent('mouseout', this, e);
27925         }
27926     },
27927     // private
27928     onFocus : function(e){
27929         if(!this.disabled){
27930             this.el.addClass("x-btn-focus");
27931         }
27932     },
27933     // private
27934     onBlur : function(e){
27935         this.el.removeClass("x-btn-focus");
27936     },
27937     // private
27938     onMouseDown : function(e){
27939         if(!this.disabled && e.button == 0){
27940             this.el.addClass("x-btn-click");
27941             Roo.get(document).on('mouseup', this.onMouseUp, this);
27942         }
27943     },
27944     // private
27945     onMouseUp : function(e){
27946         if(e.button == 0){
27947             this.el.removeClass("x-btn-click");
27948             Roo.get(document).un('mouseup', this.onMouseUp, this);
27949         }
27950     },
27951     // private
27952     onMenuShow : function(e){
27953         this.el.addClass("x-btn-menu-active");
27954     },
27955     // private
27956     onMenuHide : function(e){
27957         this.el.removeClass("x-btn-menu-active");
27958     }   
27959 });
27960
27961 // Private utility class used by Button
27962 Roo.ButtonToggleMgr = function(){
27963    var groups = {};
27964    
27965    function toggleGroup(btn, state){
27966        if(state){
27967            var g = groups[btn.toggleGroup];
27968            for(var i = 0, l = g.length; i < l; i++){
27969                if(g[i] != btn){
27970                    g[i].toggle(false);
27971                }
27972            }
27973        }
27974    }
27975    
27976    return {
27977        register : function(btn){
27978            if(!btn.toggleGroup){
27979                return;
27980            }
27981            var g = groups[btn.toggleGroup];
27982            if(!g){
27983                g = groups[btn.toggleGroup] = [];
27984            }
27985            g.push(btn);
27986            btn.on("toggle", toggleGroup);
27987        },
27988        
27989        unregister : function(btn){
27990            if(!btn.toggleGroup){
27991                return;
27992            }
27993            var g = groups[btn.toggleGroup];
27994            if(g){
27995                g.remove(btn);
27996                btn.un("toggle", toggleGroup);
27997            }
27998        }
27999    };
28000 }();/*
28001  * Based on:
28002  * Ext JS Library 1.1.1
28003  * Copyright(c) 2006-2007, Ext JS, LLC.
28004  *
28005  * Originally Released Under LGPL - original licence link has changed is not relivant.
28006  *
28007  * Fork - LGPL
28008  * <script type="text/javascript">
28009  */
28010  
28011 /**
28012  * @class Roo.SplitButton
28013  * @extends Roo.Button
28014  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28015  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28016  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28017  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28018  * @cfg {String} arrowTooltip The title attribute of the arrow
28019  * @constructor
28020  * Create a new menu button
28021  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28022  * @param {Object} config The config object
28023  */
28024 Roo.SplitButton = function(renderTo, config){
28025     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28026     /**
28027      * @event arrowclick
28028      * Fires when this button's arrow is clicked
28029      * @param {SplitButton} this
28030      * @param {EventObject} e The click event
28031      */
28032     this.addEvents({"arrowclick":true});
28033 };
28034
28035 Roo.extend(Roo.SplitButton, Roo.Button, {
28036     render : function(renderTo){
28037         // this is one sweet looking template!
28038         var tpl = new Roo.Template(
28039             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28040             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28041             '<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>',
28042             "</tbody></table></td><td>",
28043             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28044             '<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>',
28045             "</tbody></table></td></tr></table>"
28046         );
28047         var btn = tpl.append(renderTo, [this.text, this.type], true);
28048         var btnEl = btn.child("button");
28049         if(this.cls){
28050             btn.addClass(this.cls);
28051         }
28052         if(this.icon){
28053             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28054         }
28055         if(this.iconCls){
28056             btnEl.addClass(this.iconCls);
28057             if(!this.cls){
28058                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28059             }
28060         }
28061         this.el = btn;
28062         if(this.handleMouseEvents){
28063             btn.on("mouseover", this.onMouseOver, this);
28064             btn.on("mouseout", this.onMouseOut, this);
28065             btn.on("mousedown", this.onMouseDown, this);
28066             btn.on("mouseup", this.onMouseUp, this);
28067         }
28068         btn.on(this.clickEvent, this.onClick, this);
28069         if(this.tooltip){
28070             if(typeof this.tooltip == 'object'){
28071                 Roo.QuickTips.tips(Roo.apply({
28072                       target: btnEl.id
28073                 }, this.tooltip));
28074             } else {
28075                 btnEl.dom[this.tooltipType] = this.tooltip;
28076             }
28077         }
28078         if(this.arrowTooltip){
28079             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28080         }
28081         if(this.hidden){
28082             this.hide();
28083         }
28084         if(this.disabled){
28085             this.disable();
28086         }
28087         if(this.pressed){
28088             this.el.addClass("x-btn-pressed");
28089         }
28090         if(Roo.isIE && !Roo.isIE7){
28091             this.autoWidth.defer(1, this);
28092         }else{
28093             this.autoWidth();
28094         }
28095         if(this.menu){
28096             this.menu.on("show", this.onMenuShow, this);
28097             this.menu.on("hide", this.onMenuHide, this);
28098         }
28099         this.fireEvent('render', this);
28100     },
28101
28102     // private
28103     autoWidth : function(){
28104         if(this.el){
28105             var tbl = this.el.child("table:first");
28106             var tbl2 = this.el.child("table:last");
28107             this.el.setWidth("auto");
28108             tbl.setWidth("auto");
28109             if(Roo.isIE7 && Roo.isStrict){
28110                 var ib = this.el.child('button:first');
28111                 if(ib && ib.getWidth() > 20){
28112                     ib.clip();
28113                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28114                 }
28115             }
28116             if(this.minWidth){
28117                 if(this.hidden){
28118                     this.el.beginMeasure();
28119                 }
28120                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28121                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28122                 }
28123                 if(this.hidden){
28124                     this.el.endMeasure();
28125                 }
28126             }
28127             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28128         } 
28129     },
28130     /**
28131      * Sets this button's click handler
28132      * @param {Function} handler The function to call when the button is clicked
28133      * @param {Object} scope (optional) Scope for the function passed above
28134      */
28135     setHandler : function(handler, scope){
28136         this.handler = handler;
28137         this.scope = scope;  
28138     },
28139     
28140     /**
28141      * Sets this button's arrow click handler
28142      * @param {Function} handler The function to call when the arrow is clicked
28143      * @param {Object} scope (optional) Scope for the function passed above
28144      */
28145     setArrowHandler : function(handler, scope){
28146         this.arrowHandler = handler;
28147         this.scope = scope;  
28148     },
28149     
28150     /**
28151      * Focus the button
28152      */
28153     focus : function(){
28154         if(this.el){
28155             this.el.child("button:first").focus();
28156         }
28157     },
28158
28159     // private
28160     onClick : function(e){
28161         e.preventDefault();
28162         if(!this.disabled){
28163             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28164                 if(this.menu && !this.menu.isVisible()){
28165                     this.menu.show(this.el, this.menuAlign);
28166                 }
28167                 this.fireEvent("arrowclick", this, e);
28168                 if(this.arrowHandler){
28169                     this.arrowHandler.call(this.scope || this, this, e);
28170                 }
28171             }else{
28172                 this.fireEvent("click", this, e);
28173                 if(this.handler){
28174                     this.handler.call(this.scope || this, this, e);
28175                 }
28176             }
28177         }
28178     },
28179     // private
28180     onMouseDown : function(e){
28181         if(!this.disabled){
28182             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28183         }
28184     },
28185     // private
28186     onMouseUp : function(e){
28187         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28188     }   
28189 });
28190
28191
28192 // backwards compat
28193 Roo.MenuButton = Roo.SplitButton;/*
28194  * Based on:
28195  * Ext JS Library 1.1.1
28196  * Copyright(c) 2006-2007, Ext JS, LLC.
28197  *
28198  * Originally Released Under LGPL - original licence link has changed is not relivant.
28199  *
28200  * Fork - LGPL
28201  * <script type="text/javascript">
28202  */
28203
28204 /**
28205  * @class Roo.Toolbar
28206  * Basic Toolbar class.
28207  * @constructor
28208  * Creates a new Toolbar
28209  * @param {Object} container The config object
28210  */ 
28211 Roo.Toolbar = function(container, buttons, config)
28212 {
28213     /// old consturctor format still supported..
28214     if(container instanceof Array){ // omit the container for later rendering
28215         buttons = container;
28216         config = buttons;
28217         container = null;
28218     }
28219     if (typeof(container) == 'object' && container.xtype) {
28220         config = container;
28221         container = config.container;
28222         buttons = config.buttons || []; // not really - use items!!
28223     }
28224     var xitems = [];
28225     if (config && config.items) {
28226         xitems = config.items;
28227         delete config.items;
28228     }
28229     Roo.apply(this, config);
28230     this.buttons = buttons;
28231     
28232     if(container){
28233         this.render(container);
28234     }
28235     this.xitems = xitems;
28236     Roo.each(xitems, function(b) {
28237         this.add(b);
28238     }, this);
28239     
28240 };
28241
28242 Roo.Toolbar.prototype = {
28243     /**
28244      * @cfg {Array} items
28245      * array of button configs or elements to add (will be converted to a MixedCollection)
28246      */
28247     
28248     /**
28249      * @cfg {String/HTMLElement/Element} container
28250      * The id or element that will contain the toolbar
28251      */
28252     // private
28253     render : function(ct){
28254         this.el = Roo.get(ct);
28255         if(this.cls){
28256             this.el.addClass(this.cls);
28257         }
28258         // using a table allows for vertical alignment
28259         // 100% width is needed by Safari...
28260         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28261         this.tr = this.el.child("tr", true);
28262         var autoId = 0;
28263         this.items = new Roo.util.MixedCollection(false, function(o){
28264             return o.id || ("item" + (++autoId));
28265         });
28266         if(this.buttons){
28267             this.add.apply(this, this.buttons);
28268             delete this.buttons;
28269         }
28270     },
28271
28272     /**
28273      * Adds element(s) to the toolbar -- this function takes a variable number of 
28274      * arguments of mixed type and adds them to the toolbar.
28275      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28276      * <ul>
28277      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28278      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28279      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28280      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28281      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28282      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28283      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28284      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28285      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28286      * </ul>
28287      * @param {Mixed} arg2
28288      * @param {Mixed} etc.
28289      */
28290     add : function(){
28291         var a = arguments, l = a.length;
28292         for(var i = 0; i < l; i++){
28293             this._add(a[i]);
28294         }
28295     },
28296     // private..
28297     _add : function(el) {
28298         
28299         if (el.xtype) {
28300             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28301         }
28302         
28303         if (el.applyTo){ // some kind of form field
28304             return this.addField(el);
28305         } 
28306         if (el.render){ // some kind of Toolbar.Item
28307             return this.addItem(el);
28308         }
28309         if (typeof el == "string"){ // string
28310             if(el == "separator" || el == "-"){
28311                 return this.addSeparator();
28312             }
28313             if (el == " "){
28314                 return this.addSpacer();
28315             }
28316             if(el == "->"){
28317                 return this.addFill();
28318             }
28319             return this.addText(el);
28320             
28321         }
28322         if(el.tagName){ // element
28323             return this.addElement(el);
28324         }
28325         if(typeof el == "object"){ // must be button config?
28326             return this.addButton(el);
28327         }
28328         // and now what?!?!
28329         return false;
28330         
28331     },
28332     
28333     /**
28334      * Add an Xtype element
28335      * @param {Object} xtype Xtype Object
28336      * @return {Object} created Object
28337      */
28338     addxtype : function(e){
28339         return this.add(e);  
28340     },
28341     
28342     /**
28343      * Returns the Element for this toolbar.
28344      * @return {Roo.Element}
28345      */
28346     getEl : function(){
28347         return this.el;  
28348     },
28349     
28350     /**
28351      * Adds a separator
28352      * @return {Roo.Toolbar.Item} The separator item
28353      */
28354     addSeparator : function(){
28355         return this.addItem(new Roo.Toolbar.Separator());
28356     },
28357
28358     /**
28359      * Adds a spacer element
28360      * @return {Roo.Toolbar.Spacer} The spacer item
28361      */
28362     addSpacer : function(){
28363         return this.addItem(new Roo.Toolbar.Spacer());
28364     },
28365
28366     /**
28367      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28368      * @return {Roo.Toolbar.Fill} The fill item
28369      */
28370     addFill : function(){
28371         return this.addItem(new Roo.Toolbar.Fill());
28372     },
28373
28374     /**
28375      * Adds any standard HTML element to the toolbar
28376      * @param {String/HTMLElement/Element} el The element or id of the element to add
28377      * @return {Roo.Toolbar.Item} The element's item
28378      */
28379     addElement : function(el){
28380         return this.addItem(new Roo.Toolbar.Item(el));
28381     },
28382     /**
28383      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28384      * @type Roo.util.MixedCollection  
28385      */
28386     items : false,
28387      
28388     /**
28389      * Adds any Toolbar.Item or subclass
28390      * @param {Roo.Toolbar.Item} item
28391      * @return {Roo.Toolbar.Item} The item
28392      */
28393     addItem : function(item){
28394         var td = this.nextBlock();
28395         item.render(td);
28396         this.items.add(item);
28397         return item;
28398     },
28399     
28400     /**
28401      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28402      * @param {Object/Array} config A button config or array of configs
28403      * @return {Roo.Toolbar.Button/Array}
28404      */
28405     addButton : function(config){
28406         if(config instanceof Array){
28407             var buttons = [];
28408             for(var i = 0, len = config.length; i < len; i++) {
28409                 buttons.push(this.addButton(config[i]));
28410             }
28411             return buttons;
28412         }
28413         var b = config;
28414         if(!(config instanceof Roo.Toolbar.Button)){
28415             b = config.split ?
28416                 new Roo.Toolbar.SplitButton(config) :
28417                 new Roo.Toolbar.Button(config);
28418         }
28419         var td = this.nextBlock();
28420         b.render(td);
28421         this.items.add(b);
28422         return b;
28423     },
28424     
28425     /**
28426      * Adds text to the toolbar
28427      * @param {String} text The text to add
28428      * @return {Roo.Toolbar.Item} The element's item
28429      */
28430     addText : function(text){
28431         return this.addItem(new Roo.Toolbar.TextItem(text));
28432     },
28433     
28434     /**
28435      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28436      * @param {Number} index The index where the item is to be inserted
28437      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28438      * @return {Roo.Toolbar.Button/Item}
28439      */
28440     insertButton : function(index, item){
28441         if(item instanceof Array){
28442             var buttons = [];
28443             for(var i = 0, len = item.length; i < len; i++) {
28444                buttons.push(this.insertButton(index + i, item[i]));
28445             }
28446             return buttons;
28447         }
28448         if (!(item instanceof Roo.Toolbar.Button)){
28449            item = new Roo.Toolbar.Button(item);
28450         }
28451         var td = document.createElement("td");
28452         this.tr.insertBefore(td, this.tr.childNodes[index]);
28453         item.render(td);
28454         this.items.insert(index, item);
28455         return item;
28456     },
28457     
28458     /**
28459      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28460      * @param {Object} config
28461      * @return {Roo.Toolbar.Item} The element's item
28462      */
28463     addDom : function(config, returnEl){
28464         var td = this.nextBlock();
28465         Roo.DomHelper.overwrite(td, config);
28466         var ti = new Roo.Toolbar.Item(td.firstChild);
28467         ti.render(td);
28468         this.items.add(ti);
28469         return ti;
28470     },
28471
28472     /**
28473      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28474      * @type Roo.util.MixedCollection  
28475      */
28476     fields : false,
28477     
28478     /**
28479      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28480      * Note: the field should not have been rendered yet. For a field that has already been
28481      * rendered, use {@link #addElement}.
28482      * @param {Roo.form.Field} field
28483      * @return {Roo.ToolbarItem}
28484      */
28485      
28486       
28487     addField : function(field) {
28488         if (!this.fields) {
28489             var autoId = 0;
28490             this.fields = new Roo.util.MixedCollection(false, function(o){
28491                 return o.id || ("item" + (++autoId));
28492             });
28493
28494         }
28495         
28496         var td = this.nextBlock();
28497         field.render(td);
28498         var ti = new Roo.Toolbar.Item(td.firstChild);
28499         ti.render(td);
28500         this.items.add(ti);
28501         this.fields.add(field);
28502         return ti;
28503     },
28504     /**
28505      * Hide the toolbar
28506      * @method hide
28507      */
28508      
28509       
28510     hide : function()
28511     {
28512         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28513         this.el.child('div').hide();
28514     },
28515     /**
28516      * Show the toolbar
28517      * @method show
28518      */
28519     show : function()
28520     {
28521         this.el.child('div').show();
28522     },
28523       
28524     // private
28525     nextBlock : function(){
28526         var td = document.createElement("td");
28527         this.tr.appendChild(td);
28528         return td;
28529     },
28530
28531     // private
28532     destroy : function(){
28533         if(this.items){ // rendered?
28534             Roo.destroy.apply(Roo, this.items.items);
28535         }
28536         if(this.fields){ // rendered?
28537             Roo.destroy.apply(Roo, this.fields.items);
28538         }
28539         Roo.Element.uncache(this.el, this.tr);
28540     }
28541 };
28542
28543 /**
28544  * @class Roo.Toolbar.Item
28545  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28546  * @constructor
28547  * Creates a new Item
28548  * @param {HTMLElement} el 
28549  */
28550 Roo.Toolbar.Item = function(el){
28551     var cfg = {};
28552     if (typeof (el.xtype) != 'undefined') {
28553         cfg = el;
28554         el = cfg.el;
28555     }
28556     
28557     this.el = Roo.getDom(el);
28558     this.id = Roo.id(this.el);
28559     this.hidden = false;
28560     
28561     this.addEvents({
28562          /**
28563              * @event render
28564              * Fires when the button is rendered
28565              * @param {Button} this
28566              */
28567         'render': true
28568     });
28569     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28570 };
28571 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28572 //Roo.Toolbar.Item.prototype = {
28573     
28574     /**
28575      * Get this item's HTML Element
28576      * @return {HTMLElement}
28577      */
28578     getEl : function(){
28579        return this.el;  
28580     },
28581
28582     // private
28583     render : function(td){
28584         
28585          this.td = td;
28586         td.appendChild(this.el);
28587         
28588         this.fireEvent('render', this);
28589     },
28590     
28591     /**
28592      * Removes and destroys this item.
28593      */
28594     destroy : function(){
28595         this.td.parentNode.removeChild(this.td);
28596     },
28597     
28598     /**
28599      * Shows this item.
28600      */
28601     show: function(){
28602         this.hidden = false;
28603         this.td.style.display = "";
28604     },
28605     
28606     /**
28607      * Hides this item.
28608      */
28609     hide: function(){
28610         this.hidden = true;
28611         this.td.style.display = "none";
28612     },
28613     
28614     /**
28615      * Convenience function for boolean show/hide.
28616      * @param {Boolean} visible true to show/false to hide
28617      */
28618     setVisible: function(visible){
28619         if(visible) {
28620             this.show();
28621         }else{
28622             this.hide();
28623         }
28624     },
28625     
28626     /**
28627      * Try to focus this item.
28628      */
28629     focus : function(){
28630         Roo.fly(this.el).focus();
28631     },
28632     
28633     /**
28634      * Disables this item.
28635      */
28636     disable : function(){
28637         Roo.fly(this.td).addClass("x-item-disabled");
28638         this.disabled = true;
28639         this.el.disabled = true;
28640     },
28641     
28642     /**
28643      * Enables this item.
28644      */
28645     enable : function(){
28646         Roo.fly(this.td).removeClass("x-item-disabled");
28647         this.disabled = false;
28648         this.el.disabled = false;
28649     }
28650 });
28651
28652
28653 /**
28654  * @class Roo.Toolbar.Separator
28655  * @extends Roo.Toolbar.Item
28656  * A simple toolbar separator class
28657  * @constructor
28658  * Creates a new Separator
28659  */
28660 Roo.Toolbar.Separator = function(cfg){
28661     
28662     var s = document.createElement("span");
28663     s.className = "ytb-sep";
28664     if (cfg) {
28665         cfg.el = s;
28666     }
28667     
28668     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28669 };
28670 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28671     enable:Roo.emptyFn,
28672     disable:Roo.emptyFn,
28673     focus:Roo.emptyFn
28674 });
28675
28676 /**
28677  * @class Roo.Toolbar.Spacer
28678  * @extends Roo.Toolbar.Item
28679  * A simple element that adds extra horizontal space to a toolbar.
28680  * @constructor
28681  * Creates a new Spacer
28682  */
28683 Roo.Toolbar.Spacer = function(cfg){
28684     var s = document.createElement("div");
28685     s.className = "ytb-spacer";
28686     if (cfg) {
28687         cfg.el = s;
28688     }
28689     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28690 };
28691 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28692     enable:Roo.emptyFn,
28693     disable:Roo.emptyFn,
28694     focus:Roo.emptyFn
28695 });
28696
28697 /**
28698  * @class Roo.Toolbar.Fill
28699  * @extends Roo.Toolbar.Spacer
28700  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28701  * @constructor
28702  * Creates a new Spacer
28703  */
28704 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28705     // private
28706     render : function(td){
28707         td.style.width = '100%';
28708         Roo.Toolbar.Fill.superclass.render.call(this, td);
28709     }
28710 });
28711
28712 /**
28713  * @class Roo.Toolbar.TextItem
28714  * @extends Roo.Toolbar.Item
28715  * A simple class that renders text directly into a toolbar.
28716  * @constructor
28717  * Creates a new TextItem
28718  * @param {String} text
28719  */
28720 Roo.Toolbar.TextItem = function(cfg){
28721     var  text = cfg || "";
28722     if (typeof(cfg) == 'object') {
28723         text = cfg.text || "";
28724     }  else {
28725         cfg = null;
28726     }
28727     var s = document.createElement("span");
28728     s.className = "ytb-text";
28729     s.innerHTML = text;
28730     if (cfg) {
28731         cfg.el  = s;
28732     }
28733     
28734     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28735 };
28736 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28737     
28738      
28739     enable:Roo.emptyFn,
28740     disable:Roo.emptyFn,
28741     focus:Roo.emptyFn
28742 });
28743
28744 /**
28745  * @class Roo.Toolbar.Button
28746  * @extends Roo.Button
28747  * A button that renders into a toolbar.
28748  * @constructor
28749  * Creates a new Button
28750  * @param {Object} config A standard {@link Roo.Button} config object
28751  */
28752 Roo.Toolbar.Button = function(config){
28753     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28754 };
28755 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28756     render : function(td){
28757         this.td = td;
28758         Roo.Toolbar.Button.superclass.render.call(this, td);
28759     },
28760     
28761     /**
28762      * Removes and destroys this button
28763      */
28764     destroy : function(){
28765         Roo.Toolbar.Button.superclass.destroy.call(this);
28766         this.td.parentNode.removeChild(this.td);
28767     },
28768     
28769     /**
28770      * Shows this button
28771      */
28772     show: function(){
28773         this.hidden = false;
28774         this.td.style.display = "";
28775     },
28776     
28777     /**
28778      * Hides this button
28779      */
28780     hide: function(){
28781         this.hidden = true;
28782         this.td.style.display = "none";
28783     },
28784
28785     /**
28786      * Disables this item
28787      */
28788     disable : function(){
28789         Roo.fly(this.td).addClass("x-item-disabled");
28790         this.disabled = true;
28791     },
28792
28793     /**
28794      * Enables this item
28795      */
28796     enable : function(){
28797         Roo.fly(this.td).removeClass("x-item-disabled");
28798         this.disabled = false;
28799     }
28800 });
28801 // backwards compat
28802 Roo.ToolbarButton = Roo.Toolbar.Button;
28803
28804 /**
28805  * @class Roo.Toolbar.SplitButton
28806  * @extends Roo.SplitButton
28807  * A menu button that renders into a toolbar.
28808  * @constructor
28809  * Creates a new SplitButton
28810  * @param {Object} config A standard {@link Roo.SplitButton} config object
28811  */
28812 Roo.Toolbar.SplitButton = function(config){
28813     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28814 };
28815 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28816     render : function(td){
28817         this.td = td;
28818         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28819     },
28820     
28821     /**
28822      * Removes and destroys this button
28823      */
28824     destroy : function(){
28825         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28826         this.td.parentNode.removeChild(this.td);
28827     },
28828     
28829     /**
28830      * Shows this button
28831      */
28832     show: function(){
28833         this.hidden = false;
28834         this.td.style.display = "";
28835     },
28836     
28837     /**
28838      * Hides this button
28839      */
28840     hide: function(){
28841         this.hidden = true;
28842         this.td.style.display = "none";
28843     }
28844 });
28845
28846 // backwards compat
28847 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28848  * Based on:
28849  * Ext JS Library 1.1.1
28850  * Copyright(c) 2006-2007, Ext JS, LLC.
28851  *
28852  * Originally Released Under LGPL - original licence link has changed is not relivant.
28853  *
28854  * Fork - LGPL
28855  * <script type="text/javascript">
28856  */
28857  
28858 /**
28859  * @class Roo.PagingToolbar
28860  * @extends Roo.Toolbar
28861  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28862  * @constructor
28863  * Create a new PagingToolbar
28864  * @param {Object} config The config object
28865  */
28866 Roo.PagingToolbar = function(el, ds, config)
28867 {
28868     // old args format still supported... - xtype is prefered..
28869     if (typeof(el) == 'object' && el.xtype) {
28870         // created from xtype...
28871         config = el;
28872         ds = el.dataSource;
28873         el = config.container;
28874     }
28875     var items = [];
28876     if (config.items) {
28877         items = config.items;
28878         config.items = [];
28879     }
28880     
28881     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28882     this.ds = ds;
28883     this.cursor = 0;
28884     this.renderButtons(this.el);
28885     this.bind(ds);
28886     
28887     // supprot items array.
28888    
28889     Roo.each(items, function(e) {
28890         this.add(Roo.factory(e));
28891     },this);
28892     
28893 };
28894
28895 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28896     /**
28897      * @cfg {Roo.data.Store} dataSource
28898      * The underlying data store providing the paged data
28899      */
28900     /**
28901      * @cfg {String/HTMLElement/Element} container
28902      * container The id or element that will contain the toolbar
28903      */
28904     /**
28905      * @cfg {Boolean} displayInfo
28906      * True to display the displayMsg (defaults to false)
28907      */
28908     /**
28909      * @cfg {Number} pageSize
28910      * The number of records to display per page (defaults to 20)
28911      */
28912     pageSize: 20,
28913     /**
28914      * @cfg {String} displayMsg
28915      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28916      */
28917     displayMsg : 'Displaying {0} - {1} of {2}',
28918     /**
28919      * @cfg {String} emptyMsg
28920      * The message to display when no records are found (defaults to "No data to display")
28921      */
28922     emptyMsg : 'No data to display',
28923     /**
28924      * Customizable piece of the default paging text (defaults to "Page")
28925      * @type String
28926      */
28927     beforePageText : "Page",
28928     /**
28929      * Customizable piece of the default paging text (defaults to "of %0")
28930      * @type String
28931      */
28932     afterPageText : "of {0}",
28933     /**
28934      * Customizable piece of the default paging text (defaults to "First Page")
28935      * @type String
28936      */
28937     firstText : "First Page",
28938     /**
28939      * Customizable piece of the default paging text (defaults to "Previous Page")
28940      * @type String
28941      */
28942     prevText : "Previous Page",
28943     /**
28944      * Customizable piece of the default paging text (defaults to "Next Page")
28945      * @type String
28946      */
28947     nextText : "Next Page",
28948     /**
28949      * Customizable piece of the default paging text (defaults to "Last Page")
28950      * @type String
28951      */
28952     lastText : "Last Page",
28953     /**
28954      * Customizable piece of the default paging text (defaults to "Refresh")
28955      * @type String
28956      */
28957     refreshText : "Refresh",
28958
28959     // private
28960     renderButtons : function(el){
28961         Roo.PagingToolbar.superclass.render.call(this, el);
28962         this.first = this.addButton({
28963             tooltip: this.firstText,
28964             cls: "x-btn-icon x-grid-page-first",
28965             disabled: true,
28966             handler: this.onClick.createDelegate(this, ["first"])
28967         });
28968         this.prev = this.addButton({
28969             tooltip: this.prevText,
28970             cls: "x-btn-icon x-grid-page-prev",
28971             disabled: true,
28972             handler: this.onClick.createDelegate(this, ["prev"])
28973         });
28974         //this.addSeparator();
28975         this.add(this.beforePageText);
28976         this.field = Roo.get(this.addDom({
28977            tag: "input",
28978            type: "text",
28979            size: "3",
28980            value: "1",
28981            cls: "x-grid-page-number"
28982         }).el);
28983         this.field.on("keydown", this.onPagingKeydown, this);
28984         this.field.on("focus", function(){this.dom.select();});
28985         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28986         this.field.setHeight(18);
28987         //this.addSeparator();
28988         this.next = this.addButton({
28989             tooltip: this.nextText,
28990             cls: "x-btn-icon x-grid-page-next",
28991             disabled: true,
28992             handler: this.onClick.createDelegate(this, ["next"])
28993         });
28994         this.last = this.addButton({
28995             tooltip: this.lastText,
28996             cls: "x-btn-icon x-grid-page-last",
28997             disabled: true,
28998             handler: this.onClick.createDelegate(this, ["last"])
28999         });
29000         //this.addSeparator();
29001         this.loading = this.addButton({
29002             tooltip: this.refreshText,
29003             cls: "x-btn-icon x-grid-loading",
29004             handler: this.onClick.createDelegate(this, ["refresh"])
29005         });
29006
29007         if(this.displayInfo){
29008             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
29009         }
29010     },
29011
29012     // private
29013     updateInfo : function(){
29014         if(this.displayEl){
29015             var count = this.ds.getCount();
29016             var msg = count == 0 ?
29017                 this.emptyMsg :
29018                 String.format(
29019                     this.displayMsg,
29020                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29021                 );
29022             this.displayEl.update(msg);
29023         }
29024     },
29025
29026     // private
29027     onLoad : function(ds, r, o){
29028        this.cursor = o.params ? o.params.start : 0;
29029        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29030
29031        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29032        this.field.dom.value = ap;
29033        this.first.setDisabled(ap == 1);
29034        this.prev.setDisabled(ap == 1);
29035        this.next.setDisabled(ap == ps);
29036        this.last.setDisabled(ap == ps);
29037        this.loading.enable();
29038        this.updateInfo();
29039     },
29040
29041     // private
29042     getPageData : function(){
29043         var total = this.ds.getTotalCount();
29044         return {
29045             total : total,
29046             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29047             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29048         };
29049     },
29050
29051     // private
29052     onLoadError : function(){
29053         this.loading.enable();
29054     },
29055
29056     // private
29057     onPagingKeydown : function(e){
29058         var k = e.getKey();
29059         var d = this.getPageData();
29060         if(k == e.RETURN){
29061             var v = this.field.dom.value, pageNum;
29062             if(!v || isNaN(pageNum = parseInt(v, 10))){
29063                 this.field.dom.value = d.activePage;
29064                 return;
29065             }
29066             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29067             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29068             e.stopEvent();
29069         }
29070         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))
29071         {
29072           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29073           this.field.dom.value = pageNum;
29074           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29075           e.stopEvent();
29076         }
29077         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29078         {
29079           var v = this.field.dom.value, pageNum; 
29080           var increment = (e.shiftKey) ? 10 : 1;
29081           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29082             increment *= -1;
29083           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29084             this.field.dom.value = d.activePage;
29085             return;
29086           }
29087           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29088           {
29089             this.field.dom.value = parseInt(v, 10) + increment;
29090             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29091             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29092           }
29093           e.stopEvent();
29094         }
29095     },
29096
29097     // private
29098     beforeLoad : function(){
29099         if(this.loading){
29100             this.loading.disable();
29101         }
29102     },
29103
29104     // private
29105     onClick : function(which){
29106         var ds = this.ds;
29107         switch(which){
29108             case "first":
29109                 ds.load({params:{start: 0, limit: this.pageSize}});
29110             break;
29111             case "prev":
29112                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29113             break;
29114             case "next":
29115                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29116             break;
29117             case "last":
29118                 var total = ds.getTotalCount();
29119                 var extra = total % this.pageSize;
29120                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29121                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29122             break;
29123             case "refresh":
29124                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29125             break;
29126         }
29127     },
29128
29129     /**
29130      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29131      * @param {Roo.data.Store} store The data store to unbind
29132      */
29133     unbind : function(ds){
29134         ds.un("beforeload", this.beforeLoad, this);
29135         ds.un("load", this.onLoad, this);
29136         ds.un("loadexception", this.onLoadError, this);
29137         ds.un("remove", this.updateInfo, this);
29138         ds.un("add", this.updateInfo, this);
29139         this.ds = undefined;
29140     },
29141
29142     /**
29143      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29144      * @param {Roo.data.Store} store The data store to bind
29145      */
29146     bind : function(ds){
29147         ds.on("beforeload", this.beforeLoad, this);
29148         ds.on("load", this.onLoad, this);
29149         ds.on("loadexception", this.onLoadError, this);
29150         ds.on("remove", this.updateInfo, this);
29151         ds.on("add", this.updateInfo, this);
29152         this.ds = ds;
29153     }
29154 });/*
29155  * Based on:
29156  * Ext JS Library 1.1.1
29157  * Copyright(c) 2006-2007, Ext JS, LLC.
29158  *
29159  * Originally Released Under LGPL - original licence link has changed is not relivant.
29160  *
29161  * Fork - LGPL
29162  * <script type="text/javascript">
29163  */
29164
29165 /**
29166  * @class Roo.Resizable
29167  * @extends Roo.util.Observable
29168  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29169  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29170  * 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
29171  * the element will be wrapped for you automatically.</p>
29172  * <p>Here is the list of valid resize handles:</p>
29173  * <pre>
29174 Value   Description
29175 ------  -------------------
29176  'n'     north
29177  's'     south
29178  'e'     east
29179  'w'     west
29180  'nw'    northwest
29181  'sw'    southwest
29182  'se'    southeast
29183  'ne'    northeast
29184  'hd'    horizontal drag
29185  'all'   all
29186 </pre>
29187  * <p>Here's an example showing the creation of a typical Resizable:</p>
29188  * <pre><code>
29189 var resizer = new Roo.Resizable("element-id", {
29190     handles: 'all',
29191     minWidth: 200,
29192     minHeight: 100,
29193     maxWidth: 500,
29194     maxHeight: 400,
29195     pinned: true
29196 });
29197 resizer.on("resize", myHandler);
29198 </code></pre>
29199  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29200  * resizer.east.setDisplayed(false);</p>
29201  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29202  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29203  * resize operation's new size (defaults to [0, 0])
29204  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29205  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29206  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29207  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29208  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29209  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29210  * @cfg {Number} width The width of the element in pixels (defaults to null)
29211  * @cfg {Number} height The height of the element in pixels (defaults to null)
29212  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29213  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29214  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29215  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29216  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29217  * in favor of the handles config option (defaults to false)
29218  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29219  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29220  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29221  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29222  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29223  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29224  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29225  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29226  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29227  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29228  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29229  * @constructor
29230  * Create a new resizable component
29231  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29232  * @param {Object} config configuration options
29233   */
29234 Roo.Resizable = function(el, config)
29235 {
29236     this.el = Roo.get(el);
29237
29238     if(config && config.wrap){
29239         config.resizeChild = this.el;
29240         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29241         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29242         this.el.setStyle("overflow", "hidden");
29243         this.el.setPositioning(config.resizeChild.getPositioning());
29244         config.resizeChild.clearPositioning();
29245         if(!config.width || !config.height){
29246             var csize = config.resizeChild.getSize();
29247             this.el.setSize(csize.width, csize.height);
29248         }
29249         if(config.pinned && !config.adjustments){
29250             config.adjustments = "auto";
29251         }
29252     }
29253
29254     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29255     this.proxy.unselectable();
29256     this.proxy.enableDisplayMode('block');
29257
29258     Roo.apply(this, config);
29259
29260     if(this.pinned){
29261         this.disableTrackOver = true;
29262         this.el.addClass("x-resizable-pinned");
29263     }
29264     // if the element isn't positioned, make it relative
29265     var position = this.el.getStyle("position");
29266     if(position != "absolute" && position != "fixed"){
29267         this.el.setStyle("position", "relative");
29268     }
29269     if(!this.handles){ // no handles passed, must be legacy style
29270         this.handles = 's,e,se';
29271         if(this.multiDirectional){
29272             this.handles += ',n,w';
29273         }
29274     }
29275     if(this.handles == "all"){
29276         this.handles = "n s e w ne nw se sw";
29277     }
29278     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29279     var ps = Roo.Resizable.positions;
29280     for(var i = 0, len = hs.length; i < len; i++){
29281         if(hs[i] && ps[hs[i]]){
29282             var pos = ps[hs[i]];
29283             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29284         }
29285     }
29286     // legacy
29287     this.corner = this.southeast;
29288     
29289     // updateBox = the box can move..
29290     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29291         this.updateBox = true;
29292     }
29293
29294     this.activeHandle = null;
29295
29296     if(this.resizeChild){
29297         if(typeof this.resizeChild == "boolean"){
29298             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29299         }else{
29300             this.resizeChild = Roo.get(this.resizeChild, true);
29301         }
29302     }
29303     
29304     if(this.adjustments == "auto"){
29305         var rc = this.resizeChild;
29306         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29307         if(rc && (hw || hn)){
29308             rc.position("relative");
29309             rc.setLeft(hw ? hw.el.getWidth() : 0);
29310             rc.setTop(hn ? hn.el.getHeight() : 0);
29311         }
29312         this.adjustments = [
29313             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29314             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29315         ];
29316     }
29317
29318     if(this.draggable){
29319         this.dd = this.dynamic ?
29320             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29321         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29322     }
29323
29324     // public events
29325     this.addEvents({
29326         /**
29327          * @event beforeresize
29328          * Fired before resize is allowed. Set enabled to false to cancel resize.
29329          * @param {Roo.Resizable} this
29330          * @param {Roo.EventObject} e The mousedown event
29331          */
29332         "beforeresize" : true,
29333         /**
29334          * @event resizing
29335          * Fired a resizing.
29336          * @param {Roo.Resizable} this
29337          * @param {Number} x The new x position
29338          * @param {Number} y The new y position
29339          * @param {Number} w The new w width
29340          * @param {Number} h The new h hight
29341          * @param {Roo.EventObject} e The mouseup event
29342          */
29343         "resizing" : true,
29344         /**
29345          * @event resize
29346          * Fired after a resize.
29347          * @param {Roo.Resizable} this
29348          * @param {Number} width The new width
29349          * @param {Number} height The new height
29350          * @param {Roo.EventObject} e The mouseup event
29351          */
29352         "resize" : true
29353     });
29354
29355     if(this.width !== null && this.height !== null){
29356         this.resizeTo(this.width, this.height);
29357     }else{
29358         this.updateChildSize();
29359     }
29360     if(Roo.isIE){
29361         this.el.dom.style.zoom = 1;
29362     }
29363     Roo.Resizable.superclass.constructor.call(this);
29364 };
29365
29366 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29367         resizeChild : false,
29368         adjustments : [0, 0],
29369         minWidth : 5,
29370         minHeight : 5,
29371         maxWidth : 10000,
29372         maxHeight : 10000,
29373         enabled : true,
29374         animate : false,
29375         duration : .35,
29376         dynamic : false,
29377         handles : false,
29378         multiDirectional : false,
29379         disableTrackOver : false,
29380         easing : 'easeOutStrong',
29381         widthIncrement : 0,
29382         heightIncrement : 0,
29383         pinned : false,
29384         width : null,
29385         height : null,
29386         preserveRatio : false,
29387         transparent: false,
29388         minX: 0,
29389         minY: 0,
29390         draggable: false,
29391
29392         /**
29393          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29394          */
29395         constrainTo: undefined,
29396         /**
29397          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29398          */
29399         resizeRegion: undefined,
29400
29401
29402     /**
29403      * Perform a manual resize
29404      * @param {Number} width
29405      * @param {Number} height
29406      */
29407     resizeTo : function(width, height){
29408         this.el.setSize(width, height);
29409         this.updateChildSize();
29410         this.fireEvent("resize", this, width, height, null);
29411     },
29412
29413     // private
29414     startSizing : function(e, handle){
29415         this.fireEvent("beforeresize", this, e);
29416         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29417
29418             if(!this.overlay){
29419                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29420                 this.overlay.unselectable();
29421                 this.overlay.enableDisplayMode("block");
29422                 this.overlay.on("mousemove", this.onMouseMove, this);
29423                 this.overlay.on("mouseup", this.onMouseUp, this);
29424             }
29425             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29426
29427             this.resizing = true;
29428             this.startBox = this.el.getBox();
29429             this.startPoint = e.getXY();
29430             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29431                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29432
29433             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29434             this.overlay.show();
29435
29436             if(this.constrainTo) {
29437                 var ct = Roo.get(this.constrainTo);
29438                 this.resizeRegion = ct.getRegion().adjust(
29439                     ct.getFrameWidth('t'),
29440                     ct.getFrameWidth('l'),
29441                     -ct.getFrameWidth('b'),
29442                     -ct.getFrameWidth('r')
29443                 );
29444             }
29445
29446             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29447             this.proxy.show();
29448             this.proxy.setBox(this.startBox);
29449             if(!this.dynamic){
29450                 this.proxy.setStyle('visibility', 'visible');
29451             }
29452         }
29453     },
29454
29455     // private
29456     onMouseDown : function(handle, e){
29457         if(this.enabled){
29458             e.stopEvent();
29459             this.activeHandle = handle;
29460             this.startSizing(e, handle);
29461         }
29462     },
29463
29464     // private
29465     onMouseUp : function(e){
29466         var size = this.resizeElement();
29467         this.resizing = false;
29468         this.handleOut();
29469         this.overlay.hide();
29470         this.proxy.hide();
29471         this.fireEvent("resize", this, size.width, size.height, e);
29472     },
29473
29474     // private
29475     updateChildSize : function(){
29476         
29477         if(this.resizeChild){
29478             var el = this.el;
29479             var child = this.resizeChild;
29480             var adj = this.adjustments;
29481             if(el.dom.offsetWidth){
29482                 var b = el.getSize(true);
29483                 child.setSize(b.width+adj[0], b.height+adj[1]);
29484             }
29485             // Second call here for IE
29486             // The first call enables instant resizing and
29487             // the second call corrects scroll bars if they
29488             // exist
29489             if(Roo.isIE){
29490                 setTimeout(function(){
29491                     if(el.dom.offsetWidth){
29492                         var b = el.getSize(true);
29493                         child.setSize(b.width+adj[0], b.height+adj[1]);
29494                     }
29495                 }, 10);
29496             }
29497         }
29498     },
29499
29500     // private
29501     snap : function(value, inc, min){
29502         if(!inc || !value) return value;
29503         var newValue = value;
29504         var m = value % inc;
29505         if(m > 0){
29506             if(m > (inc/2)){
29507                 newValue = value + (inc-m);
29508             }else{
29509                 newValue = value - m;
29510             }
29511         }
29512         return Math.max(min, newValue);
29513     },
29514
29515     // private
29516     resizeElement : function(){
29517         var box = this.proxy.getBox();
29518         if(this.updateBox){
29519             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29520         }else{
29521             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29522         }
29523         this.updateChildSize();
29524         if(!this.dynamic){
29525             this.proxy.hide();
29526         }
29527         return box;
29528     },
29529
29530     // private
29531     constrain : function(v, diff, m, mx){
29532         if(v - diff < m){
29533             diff = v - m;
29534         }else if(v - diff > mx){
29535             diff = mx - v;
29536         }
29537         return diff;
29538     },
29539
29540     // private
29541     onMouseMove : function(e){
29542         
29543         if(this.enabled){
29544             try{// try catch so if something goes wrong the user doesn't get hung
29545
29546             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29547                 return;
29548             }
29549
29550             //var curXY = this.startPoint;
29551             var curSize = this.curSize || this.startBox;
29552             var x = this.startBox.x, y = this.startBox.y;
29553             var ox = x, oy = y;
29554             var w = curSize.width, h = curSize.height;
29555             var ow = w, oh = h;
29556             var mw = this.minWidth, mh = this.minHeight;
29557             var mxw = this.maxWidth, mxh = this.maxHeight;
29558             var wi = this.widthIncrement;
29559             var hi = this.heightIncrement;
29560
29561             var eventXY = e.getXY();
29562             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29563             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29564
29565             var pos = this.activeHandle.position;
29566
29567             switch(pos){
29568                 case "east":
29569                     w += diffX;
29570                     w = Math.min(Math.max(mw, w), mxw);
29571                     break;
29572              
29573                 case "south":
29574                     h += diffY;
29575                     h = Math.min(Math.max(mh, h), mxh);
29576                     break;
29577                 case "southeast":
29578                     w += diffX;
29579                     h += diffY;
29580                     w = Math.min(Math.max(mw, w), mxw);
29581                     h = Math.min(Math.max(mh, h), mxh);
29582                     break;
29583                 case "north":
29584                     diffY = this.constrain(h, diffY, mh, mxh);
29585                     y += diffY;
29586                     h -= diffY;
29587                     break;
29588                 case "hdrag":
29589                     
29590                     if (wi) {
29591                         var adiffX = Math.abs(diffX);
29592                         var sub = (adiffX % wi); // how much 
29593                         if (sub > (wi/2)) { // far enough to snap
29594                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29595                         } else {
29596                             // remove difference.. 
29597                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29598                         }
29599                     }
29600                     x += diffX;
29601                     x = Math.max(this.minX, x);
29602                     break;
29603                 case "west":
29604                     diffX = this.constrain(w, diffX, mw, mxw);
29605                     x += diffX;
29606                     w -= diffX;
29607                     break;
29608                 case "northeast":
29609                     w += diffX;
29610                     w = Math.min(Math.max(mw, w), mxw);
29611                     diffY = this.constrain(h, diffY, mh, mxh);
29612                     y += diffY;
29613                     h -= diffY;
29614                     break;
29615                 case "northwest":
29616                     diffX = this.constrain(w, diffX, mw, mxw);
29617                     diffY = this.constrain(h, diffY, mh, mxh);
29618                     y += diffY;
29619                     h -= diffY;
29620                     x += diffX;
29621                     w -= diffX;
29622                     break;
29623                case "southwest":
29624                     diffX = this.constrain(w, diffX, mw, mxw);
29625                     h += diffY;
29626                     h = Math.min(Math.max(mh, h), mxh);
29627                     x += diffX;
29628                     w -= diffX;
29629                     break;
29630             }
29631
29632             var sw = this.snap(w, wi, mw);
29633             var sh = this.snap(h, hi, mh);
29634             if(sw != w || sh != h){
29635                 switch(pos){
29636                     case "northeast":
29637                         y -= sh - h;
29638                     break;
29639                     case "north":
29640                         y -= sh - h;
29641                         break;
29642                     case "southwest":
29643                         x -= sw - w;
29644                     break;
29645                     case "west":
29646                         x -= sw - w;
29647                         break;
29648                     case "northwest":
29649                         x -= sw - w;
29650                         y -= sh - h;
29651                     break;
29652                 }
29653                 w = sw;
29654                 h = sh;
29655             }
29656
29657             if(this.preserveRatio){
29658                 switch(pos){
29659                     case "southeast":
29660                     case "east":
29661                         h = oh * (w/ow);
29662                         h = Math.min(Math.max(mh, h), mxh);
29663                         w = ow * (h/oh);
29664                        break;
29665                     case "south":
29666                         w = ow * (h/oh);
29667                         w = Math.min(Math.max(mw, w), mxw);
29668                         h = oh * (w/ow);
29669                         break;
29670                     case "northeast":
29671                         w = ow * (h/oh);
29672                         w = Math.min(Math.max(mw, w), mxw);
29673                         h = oh * (w/ow);
29674                     break;
29675                     case "north":
29676                         var tw = w;
29677                         w = ow * (h/oh);
29678                         w = Math.min(Math.max(mw, w), mxw);
29679                         h = oh * (w/ow);
29680                         x += (tw - w) / 2;
29681                         break;
29682                     case "southwest":
29683                         h = oh * (w/ow);
29684                         h = Math.min(Math.max(mh, h), mxh);
29685                         var tw = w;
29686                         w = ow * (h/oh);
29687                         x += tw - w;
29688                         break;
29689                     case "west":
29690                         var th = h;
29691                         h = oh * (w/ow);
29692                         h = Math.min(Math.max(mh, h), mxh);
29693                         y += (th - h) / 2;
29694                         var tw = w;
29695                         w = ow * (h/oh);
29696                         x += tw - w;
29697                        break;
29698                     case "northwest":
29699                         var tw = w;
29700                         var th = h;
29701                         h = oh * (w/ow);
29702                         h = Math.min(Math.max(mh, h), mxh);
29703                         w = ow * (h/oh);
29704                         y += th - h;
29705                         x += tw - w;
29706                        break;
29707
29708                 }
29709             }
29710             if (pos == 'hdrag') {
29711                 w = ow;
29712             }
29713             this.proxy.setBounds(x, y, w, h);
29714             if(this.dynamic){
29715                 this.resizeElement();
29716             }
29717             }catch(e){}
29718         }
29719         this.fireEvent("resizing", this, x, y, w, h, e);
29720     },
29721
29722     // private
29723     handleOver : function(){
29724         if(this.enabled){
29725             this.el.addClass("x-resizable-over");
29726         }
29727     },
29728
29729     // private
29730     handleOut : function(){
29731         if(!this.resizing){
29732             this.el.removeClass("x-resizable-over");
29733         }
29734     },
29735
29736     /**
29737      * Returns the element this component is bound to.
29738      * @return {Roo.Element}
29739      */
29740     getEl : function(){
29741         return this.el;
29742     },
29743
29744     /**
29745      * Returns the resizeChild element (or null).
29746      * @return {Roo.Element}
29747      */
29748     getResizeChild : function(){
29749         return this.resizeChild;
29750     },
29751     groupHandler : function()
29752     {
29753         
29754     },
29755     /**
29756      * Destroys this resizable. If the element was wrapped and
29757      * removeEl is not true then the element remains.
29758      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29759      */
29760     destroy : function(removeEl){
29761         this.proxy.remove();
29762         if(this.overlay){
29763             this.overlay.removeAllListeners();
29764             this.overlay.remove();
29765         }
29766         var ps = Roo.Resizable.positions;
29767         for(var k in ps){
29768             if(typeof ps[k] != "function" && this[ps[k]]){
29769                 var h = this[ps[k]];
29770                 h.el.removeAllListeners();
29771                 h.el.remove();
29772             }
29773         }
29774         if(removeEl){
29775             this.el.update("");
29776             this.el.remove();
29777         }
29778     }
29779 });
29780
29781 // private
29782 // hash to map config positions to true positions
29783 Roo.Resizable.positions = {
29784     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29785     hd: "hdrag"
29786 };
29787
29788 // private
29789 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29790     if(!this.tpl){
29791         // only initialize the template if resizable is used
29792         var tpl = Roo.DomHelper.createTemplate(
29793             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29794         );
29795         tpl.compile();
29796         Roo.Resizable.Handle.prototype.tpl = tpl;
29797     }
29798     this.position = pos;
29799     this.rz = rz;
29800     // show north drag fro topdra
29801     var handlepos = pos == 'hdrag' ? 'north' : pos;
29802     
29803     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29804     if (pos == 'hdrag') {
29805         this.el.setStyle('cursor', 'pointer');
29806     }
29807     this.el.unselectable();
29808     if(transparent){
29809         this.el.setOpacity(0);
29810     }
29811     this.el.on("mousedown", this.onMouseDown, this);
29812     if(!disableTrackOver){
29813         this.el.on("mouseover", this.onMouseOver, this);
29814         this.el.on("mouseout", this.onMouseOut, this);
29815     }
29816 };
29817
29818 // private
29819 Roo.Resizable.Handle.prototype = {
29820     afterResize : function(rz){
29821         Roo.log('after?');
29822         // do nothing
29823     },
29824     // private
29825     onMouseDown : function(e){
29826         this.rz.onMouseDown(this, e);
29827     },
29828     // private
29829     onMouseOver : function(e){
29830         this.rz.handleOver(this, e);
29831     },
29832     // private
29833     onMouseOut : function(e){
29834         this.rz.handleOut(this, e);
29835     }
29836 };/*
29837  * Based on:
29838  * Ext JS Library 1.1.1
29839  * Copyright(c) 2006-2007, Ext JS, LLC.
29840  *
29841  * Originally Released Under LGPL - original licence link has changed is not relivant.
29842  *
29843  * Fork - LGPL
29844  * <script type="text/javascript">
29845  */
29846
29847 /**
29848  * @class Roo.Editor
29849  * @extends Roo.Component
29850  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29851  * @constructor
29852  * Create a new Editor
29853  * @param {Roo.form.Field} field The Field object (or descendant)
29854  * @param {Object} config The config object
29855  */
29856 Roo.Editor = function(field, config){
29857     Roo.Editor.superclass.constructor.call(this, config);
29858     this.field = field;
29859     this.addEvents({
29860         /**
29861              * @event beforestartedit
29862              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29863              * false from the handler of this event.
29864              * @param {Editor} this
29865              * @param {Roo.Element} boundEl The underlying element bound to this editor
29866              * @param {Mixed} value The field value being set
29867              */
29868         "beforestartedit" : true,
29869         /**
29870              * @event startedit
29871              * Fires when this editor is displayed
29872              * @param {Roo.Element} boundEl The underlying element bound to this editor
29873              * @param {Mixed} value The starting field value
29874              */
29875         "startedit" : true,
29876         /**
29877              * @event beforecomplete
29878              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29879              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29880              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29881              * event will not fire since no edit actually occurred.
29882              * @param {Editor} this
29883              * @param {Mixed} value The current field value
29884              * @param {Mixed} startValue The original field value
29885              */
29886         "beforecomplete" : true,
29887         /**
29888              * @event complete
29889              * Fires after editing is complete and any changed value has been written to the underlying field.
29890              * @param {Editor} this
29891              * @param {Mixed} value The current field value
29892              * @param {Mixed} startValue The original field value
29893              */
29894         "complete" : true,
29895         /**
29896          * @event specialkey
29897          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29898          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29899          * @param {Roo.form.Field} this
29900          * @param {Roo.EventObject} e The event object
29901          */
29902         "specialkey" : true
29903     });
29904 };
29905
29906 Roo.extend(Roo.Editor, Roo.Component, {
29907     /**
29908      * @cfg {Boolean/String} autosize
29909      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29910      * or "height" to adopt the height only (defaults to false)
29911      */
29912     /**
29913      * @cfg {Boolean} revertInvalid
29914      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29915      * validation fails (defaults to true)
29916      */
29917     /**
29918      * @cfg {Boolean} ignoreNoChange
29919      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29920      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29921      * will never be ignored.
29922      */
29923     /**
29924      * @cfg {Boolean} hideEl
29925      * False to keep the bound element visible while the editor is displayed (defaults to true)
29926      */
29927     /**
29928      * @cfg {Mixed} value
29929      * The data value of the underlying field (defaults to "")
29930      */
29931     value : "",
29932     /**
29933      * @cfg {String} alignment
29934      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29935      */
29936     alignment: "c-c?",
29937     /**
29938      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29939      * for bottom-right shadow (defaults to "frame")
29940      */
29941     shadow : "frame",
29942     /**
29943      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29944      */
29945     constrain : false,
29946     /**
29947      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29948      */
29949     completeOnEnter : false,
29950     /**
29951      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29952      */
29953     cancelOnEsc : false,
29954     /**
29955      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29956      */
29957     updateEl : false,
29958
29959     // private
29960     onRender : function(ct, position){
29961         this.el = new Roo.Layer({
29962             shadow: this.shadow,
29963             cls: "x-editor",
29964             parentEl : ct,
29965             shim : this.shim,
29966             shadowOffset:4,
29967             id: this.id,
29968             constrain: this.constrain
29969         });
29970         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29971         if(this.field.msgTarget != 'title'){
29972             this.field.msgTarget = 'qtip';
29973         }
29974         this.field.render(this.el);
29975         if(Roo.isGecko){
29976             this.field.el.dom.setAttribute('autocomplete', 'off');
29977         }
29978         this.field.on("specialkey", this.onSpecialKey, this);
29979         if(this.swallowKeys){
29980             this.field.el.swallowEvent(['keydown','keypress']);
29981         }
29982         this.field.show();
29983         this.field.on("blur", this.onBlur, this);
29984         if(this.field.grow){
29985             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29986         }
29987     },
29988
29989     onSpecialKey : function(field, e)
29990     {
29991         //Roo.log('editor onSpecialKey');
29992         if(this.completeOnEnter && e.getKey() == e.ENTER){
29993             e.stopEvent();
29994             this.completeEdit();
29995             return;
29996         }
29997         // do not fire special key otherwise it might hide close the editor...
29998         if(e.getKey() == e.ENTER){    
29999             return;
30000         }
30001         if(this.cancelOnEsc && e.getKey() == e.ESC){
30002             this.cancelEdit();
30003             return;
30004         } 
30005         this.fireEvent('specialkey', field, e);
30006     
30007     },
30008
30009     /**
30010      * Starts the editing process and shows the editor.
30011      * @param {String/HTMLElement/Element} el The element to edit
30012      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30013       * to the innerHTML of el.
30014      */
30015     startEdit : function(el, value){
30016         if(this.editing){
30017             this.completeEdit();
30018         }
30019         this.boundEl = Roo.get(el);
30020         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30021         if(!this.rendered){
30022             this.render(this.parentEl || document.body);
30023         }
30024         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30025             return;
30026         }
30027         this.startValue = v;
30028         this.field.setValue(v);
30029         if(this.autoSize){
30030             var sz = this.boundEl.getSize();
30031             switch(this.autoSize){
30032                 case "width":
30033                 this.setSize(sz.width,  "");
30034                 break;
30035                 case "height":
30036                 this.setSize("",  sz.height);
30037                 break;
30038                 default:
30039                 this.setSize(sz.width,  sz.height);
30040             }
30041         }
30042         this.el.alignTo(this.boundEl, this.alignment);
30043         this.editing = true;
30044         if(Roo.QuickTips){
30045             Roo.QuickTips.disable();
30046         }
30047         this.show();
30048     },
30049
30050     /**
30051      * Sets the height and width of this editor.
30052      * @param {Number} width The new width
30053      * @param {Number} height The new height
30054      */
30055     setSize : function(w, h){
30056         this.field.setSize(w, h);
30057         if(this.el){
30058             this.el.sync();
30059         }
30060     },
30061
30062     /**
30063      * Realigns the editor to the bound field based on the current alignment config value.
30064      */
30065     realign : function(){
30066         this.el.alignTo(this.boundEl, this.alignment);
30067     },
30068
30069     /**
30070      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30071      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30072      */
30073     completeEdit : function(remainVisible){
30074         if(!this.editing){
30075             return;
30076         }
30077         var v = this.getValue();
30078         if(this.revertInvalid !== false && !this.field.isValid()){
30079             v = this.startValue;
30080             this.cancelEdit(true);
30081         }
30082         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30083             this.editing = false;
30084             this.hide();
30085             return;
30086         }
30087         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30088             this.editing = false;
30089             if(this.updateEl && this.boundEl){
30090                 this.boundEl.update(v);
30091             }
30092             if(remainVisible !== true){
30093                 this.hide();
30094             }
30095             this.fireEvent("complete", this, v, this.startValue);
30096         }
30097     },
30098
30099     // private
30100     onShow : function(){
30101         this.el.show();
30102         if(this.hideEl !== false){
30103             this.boundEl.hide();
30104         }
30105         this.field.show();
30106         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30107             this.fixIEFocus = true;
30108             this.deferredFocus.defer(50, this);
30109         }else{
30110             this.field.focus();
30111         }
30112         this.fireEvent("startedit", this.boundEl, this.startValue);
30113     },
30114
30115     deferredFocus : function(){
30116         if(this.editing){
30117             this.field.focus();
30118         }
30119     },
30120
30121     /**
30122      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30123      * reverted to the original starting value.
30124      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30125      * cancel (defaults to false)
30126      */
30127     cancelEdit : function(remainVisible){
30128         if(this.editing){
30129             this.setValue(this.startValue);
30130             if(remainVisible !== true){
30131                 this.hide();
30132             }
30133         }
30134     },
30135
30136     // private
30137     onBlur : function(){
30138         if(this.allowBlur !== true && this.editing){
30139             this.completeEdit();
30140         }
30141     },
30142
30143     // private
30144     onHide : function(){
30145         if(this.editing){
30146             this.completeEdit();
30147             return;
30148         }
30149         this.field.blur();
30150         if(this.field.collapse){
30151             this.field.collapse();
30152         }
30153         this.el.hide();
30154         if(this.hideEl !== false){
30155             this.boundEl.show();
30156         }
30157         if(Roo.QuickTips){
30158             Roo.QuickTips.enable();
30159         }
30160     },
30161
30162     /**
30163      * Sets the data value of the editor
30164      * @param {Mixed} value Any valid value supported by the underlying field
30165      */
30166     setValue : function(v){
30167         this.field.setValue(v);
30168     },
30169
30170     /**
30171      * Gets the data value of the editor
30172      * @return {Mixed} The data value
30173      */
30174     getValue : function(){
30175         return this.field.getValue();
30176     }
30177 });/*
30178  * Based on:
30179  * Ext JS Library 1.1.1
30180  * Copyright(c) 2006-2007, Ext JS, LLC.
30181  *
30182  * Originally Released Under LGPL - original licence link has changed is not relivant.
30183  *
30184  * Fork - LGPL
30185  * <script type="text/javascript">
30186  */
30187  
30188 /**
30189  * @class Roo.BasicDialog
30190  * @extends Roo.util.Observable
30191  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30192  * <pre><code>
30193 var dlg = new Roo.BasicDialog("my-dlg", {
30194     height: 200,
30195     width: 300,
30196     minHeight: 100,
30197     minWidth: 150,
30198     modal: true,
30199     proxyDrag: true,
30200     shadow: true
30201 });
30202 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30203 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30204 dlg.addButton('Cancel', dlg.hide, dlg);
30205 dlg.show();
30206 </code></pre>
30207   <b>A Dialog should always be a direct child of the body element.</b>
30208  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30209  * @cfg {String} title Default text to display in the title bar (defaults to null)
30210  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30211  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30212  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30213  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30214  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30215  * (defaults to null with no animation)
30216  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30217  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30218  * property for valid values (defaults to 'all')
30219  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30220  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30221  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30222  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30223  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30224  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30225  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30226  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30227  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30228  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30229  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30230  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30231  * draggable = true (defaults to false)
30232  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30233  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30234  * shadow (defaults to false)
30235  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30236  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30237  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30238  * @cfg {Array} buttons Array of buttons
30239  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30240  * @constructor
30241  * Create a new BasicDialog.
30242  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30243  * @param {Object} config Configuration options
30244  */
30245 Roo.BasicDialog = function(el, config){
30246     this.el = Roo.get(el);
30247     var dh = Roo.DomHelper;
30248     if(!this.el && config && config.autoCreate){
30249         if(typeof config.autoCreate == "object"){
30250             if(!config.autoCreate.id){
30251                 config.autoCreate.id = el;
30252             }
30253             this.el = dh.append(document.body,
30254                         config.autoCreate, true);
30255         }else{
30256             this.el = dh.append(document.body,
30257                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30258         }
30259     }
30260     el = this.el;
30261     el.setDisplayed(true);
30262     el.hide = this.hideAction;
30263     this.id = el.id;
30264     el.addClass("x-dlg");
30265
30266     Roo.apply(this, config);
30267
30268     this.proxy = el.createProxy("x-dlg-proxy");
30269     this.proxy.hide = this.hideAction;
30270     this.proxy.setOpacity(.5);
30271     this.proxy.hide();
30272
30273     if(config.width){
30274         el.setWidth(config.width);
30275     }
30276     if(config.height){
30277         el.setHeight(config.height);
30278     }
30279     this.size = el.getSize();
30280     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30281         this.xy = [config.x,config.y];
30282     }else{
30283         this.xy = el.getCenterXY(true);
30284     }
30285     /** The header element @type Roo.Element */
30286     this.header = el.child("> .x-dlg-hd");
30287     /** The body element @type Roo.Element */
30288     this.body = el.child("> .x-dlg-bd");
30289     /** The footer element @type Roo.Element */
30290     this.footer = el.child("> .x-dlg-ft");
30291
30292     if(!this.header){
30293         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30294     }
30295     if(!this.body){
30296         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30297     }
30298
30299     this.header.unselectable();
30300     if(this.title){
30301         this.header.update(this.title);
30302     }
30303     // this element allows the dialog to be focused for keyboard event
30304     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30305     this.focusEl.swallowEvent("click", true);
30306
30307     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30308
30309     // wrap the body and footer for special rendering
30310     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30311     if(this.footer){
30312         this.bwrap.dom.appendChild(this.footer.dom);
30313     }
30314
30315     this.bg = this.el.createChild({
30316         tag: "div", cls:"x-dlg-bg",
30317         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30318     });
30319     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30320
30321
30322     if(this.autoScroll !== false && !this.autoTabs){
30323         this.body.setStyle("overflow", "auto");
30324     }
30325
30326     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30327
30328     if(this.closable !== false){
30329         this.el.addClass("x-dlg-closable");
30330         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30331         this.close.on("click", this.closeClick, this);
30332         this.close.addClassOnOver("x-dlg-close-over");
30333     }
30334     if(this.collapsible !== false){
30335         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30336         this.collapseBtn.on("click", this.collapseClick, this);
30337         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30338         this.header.on("dblclick", this.collapseClick, this);
30339     }
30340     if(this.resizable !== false){
30341         this.el.addClass("x-dlg-resizable");
30342         this.resizer = new Roo.Resizable(el, {
30343             minWidth: this.minWidth || 80,
30344             minHeight:this.minHeight || 80,
30345             handles: this.resizeHandles || "all",
30346             pinned: true
30347         });
30348         this.resizer.on("beforeresize", this.beforeResize, this);
30349         this.resizer.on("resize", this.onResize, this);
30350     }
30351     if(this.draggable !== false){
30352         el.addClass("x-dlg-draggable");
30353         if (!this.proxyDrag) {
30354             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30355         }
30356         else {
30357             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30358         }
30359         dd.setHandleElId(this.header.id);
30360         dd.endDrag = this.endMove.createDelegate(this);
30361         dd.startDrag = this.startMove.createDelegate(this);
30362         dd.onDrag = this.onDrag.createDelegate(this);
30363         dd.scroll = false;
30364         this.dd = dd;
30365     }
30366     if(this.modal){
30367         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30368         this.mask.enableDisplayMode("block");
30369         this.mask.hide();
30370         this.el.addClass("x-dlg-modal");
30371     }
30372     if(this.shadow){
30373         this.shadow = new Roo.Shadow({
30374             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30375             offset : this.shadowOffset
30376         });
30377     }else{
30378         this.shadowOffset = 0;
30379     }
30380     if(Roo.useShims && this.shim !== false){
30381         this.shim = this.el.createShim();
30382         this.shim.hide = this.hideAction;
30383         this.shim.hide();
30384     }else{
30385         this.shim = false;
30386     }
30387     if(this.autoTabs){
30388         this.initTabs();
30389     }
30390     if (this.buttons) { 
30391         var bts= this.buttons;
30392         this.buttons = [];
30393         Roo.each(bts, function(b) {
30394             this.addButton(b);
30395         }, this);
30396     }
30397     
30398     
30399     this.addEvents({
30400         /**
30401          * @event keydown
30402          * Fires when a key is pressed
30403          * @param {Roo.BasicDialog} this
30404          * @param {Roo.EventObject} e
30405          */
30406         "keydown" : true,
30407         /**
30408          * @event move
30409          * Fires when this dialog is moved by the user.
30410          * @param {Roo.BasicDialog} this
30411          * @param {Number} x The new page X
30412          * @param {Number} y The new page Y
30413          */
30414         "move" : true,
30415         /**
30416          * @event resize
30417          * Fires when this dialog is resized by the user.
30418          * @param {Roo.BasicDialog} this
30419          * @param {Number} width The new width
30420          * @param {Number} height The new height
30421          */
30422         "resize" : true,
30423         /**
30424          * @event beforehide
30425          * Fires before this dialog is hidden.
30426          * @param {Roo.BasicDialog} this
30427          */
30428         "beforehide" : true,
30429         /**
30430          * @event hide
30431          * Fires when this dialog is hidden.
30432          * @param {Roo.BasicDialog} this
30433          */
30434         "hide" : true,
30435         /**
30436          * @event beforeshow
30437          * Fires before this dialog is shown.
30438          * @param {Roo.BasicDialog} this
30439          */
30440         "beforeshow" : true,
30441         /**
30442          * @event show
30443          * Fires when this dialog is shown.
30444          * @param {Roo.BasicDialog} this
30445          */
30446         "show" : true
30447     });
30448     el.on("keydown", this.onKeyDown, this);
30449     el.on("mousedown", this.toFront, this);
30450     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30451     this.el.hide();
30452     Roo.DialogManager.register(this);
30453     Roo.BasicDialog.superclass.constructor.call(this);
30454 };
30455
30456 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30457     shadowOffset: Roo.isIE ? 6 : 5,
30458     minHeight: 80,
30459     minWidth: 200,
30460     minButtonWidth: 75,
30461     defaultButton: null,
30462     buttonAlign: "right",
30463     tabTag: 'div',
30464     firstShow: true,
30465
30466     /**
30467      * Sets the dialog title text
30468      * @param {String} text The title text to display
30469      * @return {Roo.BasicDialog} this
30470      */
30471     setTitle : function(text){
30472         this.header.update(text);
30473         return this;
30474     },
30475
30476     // private
30477     closeClick : function(){
30478         this.hide();
30479     },
30480
30481     // private
30482     collapseClick : function(){
30483         this[this.collapsed ? "expand" : "collapse"]();
30484     },
30485
30486     /**
30487      * Collapses the dialog to its minimized state (only the title bar is visible).
30488      * Equivalent to the user clicking the collapse dialog button.
30489      */
30490     collapse : function(){
30491         if(!this.collapsed){
30492             this.collapsed = true;
30493             this.el.addClass("x-dlg-collapsed");
30494             this.restoreHeight = this.el.getHeight();
30495             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30496         }
30497     },
30498
30499     /**
30500      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30501      * clicking the expand dialog button.
30502      */
30503     expand : function(){
30504         if(this.collapsed){
30505             this.collapsed = false;
30506             this.el.removeClass("x-dlg-collapsed");
30507             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30508         }
30509     },
30510
30511     /**
30512      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30513      * @return {Roo.TabPanel} The tabs component
30514      */
30515     initTabs : function(){
30516         var tabs = this.getTabs();
30517         while(tabs.getTab(0)){
30518             tabs.removeTab(0);
30519         }
30520         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30521             var dom = el.dom;
30522             tabs.addTab(Roo.id(dom), dom.title);
30523             dom.title = "";
30524         });
30525         tabs.activate(0);
30526         return tabs;
30527     },
30528
30529     // private
30530     beforeResize : function(){
30531         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30532     },
30533
30534     // private
30535     onResize : function(){
30536         this.refreshSize();
30537         this.syncBodyHeight();
30538         this.adjustAssets();
30539         this.focus();
30540         this.fireEvent("resize", this, this.size.width, this.size.height);
30541     },
30542
30543     // private
30544     onKeyDown : function(e){
30545         if(this.isVisible()){
30546             this.fireEvent("keydown", this, e);
30547         }
30548     },
30549
30550     /**
30551      * Resizes the dialog.
30552      * @param {Number} width
30553      * @param {Number} height
30554      * @return {Roo.BasicDialog} this
30555      */
30556     resizeTo : function(width, height){
30557         this.el.setSize(width, height);
30558         this.size = {width: width, height: height};
30559         this.syncBodyHeight();
30560         if(this.fixedcenter){
30561             this.center();
30562         }
30563         if(this.isVisible()){
30564             this.constrainXY();
30565             this.adjustAssets();
30566         }
30567         this.fireEvent("resize", this, width, height);
30568         return this;
30569     },
30570
30571
30572     /**
30573      * Resizes the dialog to fit the specified content size.
30574      * @param {Number} width
30575      * @param {Number} height
30576      * @return {Roo.BasicDialog} this
30577      */
30578     setContentSize : function(w, h){
30579         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30580         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30581         //if(!this.el.isBorderBox()){
30582             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30583             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30584         //}
30585         if(this.tabs){
30586             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30587             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30588         }
30589         this.resizeTo(w, h);
30590         return this;
30591     },
30592
30593     /**
30594      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30595      * executed in response to a particular key being pressed while the dialog is active.
30596      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30597      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30598      * @param {Function} fn The function to call
30599      * @param {Object} scope (optional) The scope of the function
30600      * @return {Roo.BasicDialog} this
30601      */
30602     addKeyListener : function(key, fn, scope){
30603         var keyCode, shift, ctrl, alt;
30604         if(typeof key == "object" && !(key instanceof Array)){
30605             keyCode = key["key"];
30606             shift = key["shift"];
30607             ctrl = key["ctrl"];
30608             alt = key["alt"];
30609         }else{
30610             keyCode = key;
30611         }
30612         var handler = function(dlg, e){
30613             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30614                 var k = e.getKey();
30615                 if(keyCode instanceof Array){
30616                     for(var i = 0, len = keyCode.length; i < len; i++){
30617                         if(keyCode[i] == k){
30618                           fn.call(scope || window, dlg, k, e);
30619                           return;
30620                         }
30621                     }
30622                 }else{
30623                     if(k == keyCode){
30624                         fn.call(scope || window, dlg, k, e);
30625                     }
30626                 }
30627             }
30628         };
30629         this.on("keydown", handler);
30630         return this;
30631     },
30632
30633     /**
30634      * Returns the TabPanel component (creates it if it doesn't exist).
30635      * Note: If you wish to simply check for the existence of tabs without creating them,
30636      * check for a null 'tabs' property.
30637      * @return {Roo.TabPanel} The tabs component
30638      */
30639     getTabs : function(){
30640         if(!this.tabs){
30641             this.el.addClass("x-dlg-auto-tabs");
30642             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30643             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30644         }
30645         return this.tabs;
30646     },
30647
30648     /**
30649      * Adds a button to the footer section of the dialog.
30650      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30651      * object or a valid Roo.DomHelper element config
30652      * @param {Function} handler The function called when the button is clicked
30653      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30654      * @return {Roo.Button} The new button
30655      */
30656     addButton : function(config, handler, scope){
30657         var dh = Roo.DomHelper;
30658         if(!this.footer){
30659             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30660         }
30661         if(!this.btnContainer){
30662             var tb = this.footer.createChild({
30663
30664                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30665                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30666             }, null, true);
30667             this.btnContainer = tb.firstChild.firstChild.firstChild;
30668         }
30669         var bconfig = {
30670             handler: handler,
30671             scope: scope,
30672             minWidth: this.minButtonWidth,
30673             hideParent:true
30674         };
30675         if(typeof config == "string"){
30676             bconfig.text = config;
30677         }else{
30678             if(config.tag){
30679                 bconfig.dhconfig = config;
30680             }else{
30681                 Roo.apply(bconfig, config);
30682             }
30683         }
30684         var fc = false;
30685         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30686             bconfig.position = Math.max(0, bconfig.position);
30687             fc = this.btnContainer.childNodes[bconfig.position];
30688         }
30689          
30690         var btn = new Roo.Button(
30691             fc ? 
30692                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30693                 : this.btnContainer.appendChild(document.createElement("td")),
30694             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30695             bconfig
30696         );
30697         this.syncBodyHeight();
30698         if(!this.buttons){
30699             /**
30700              * Array of all the buttons that have been added to this dialog via addButton
30701              * @type Array
30702              */
30703             this.buttons = [];
30704         }
30705         this.buttons.push(btn);
30706         return btn;
30707     },
30708
30709     /**
30710      * Sets the default button to be focused when the dialog is displayed.
30711      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30712      * @return {Roo.BasicDialog} this
30713      */
30714     setDefaultButton : function(btn){
30715         this.defaultButton = btn;
30716         return this;
30717     },
30718
30719     // private
30720     getHeaderFooterHeight : function(safe){
30721         var height = 0;
30722         if(this.header){
30723            height += this.header.getHeight();
30724         }
30725         if(this.footer){
30726            var fm = this.footer.getMargins();
30727             height += (this.footer.getHeight()+fm.top+fm.bottom);
30728         }
30729         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30730         height += this.centerBg.getPadding("tb");
30731         return height;
30732     },
30733
30734     // private
30735     syncBodyHeight : function()
30736     {
30737         var bd = this.body, // the text
30738             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30739             bw = this.bwrap;
30740         var height = this.size.height - this.getHeaderFooterHeight(false);
30741         bd.setHeight(height-bd.getMargins("tb"));
30742         var hh = this.header.getHeight();
30743         var h = this.size.height-hh;
30744         cb.setHeight(h);
30745         
30746         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30747         bw.setHeight(h-cb.getPadding("tb"));
30748         
30749         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30750         bd.setWidth(bw.getWidth(true));
30751         if(this.tabs){
30752             this.tabs.syncHeight();
30753             if(Roo.isIE){
30754                 this.tabs.el.repaint();
30755             }
30756         }
30757     },
30758
30759     /**
30760      * Restores the previous state of the dialog if Roo.state is configured.
30761      * @return {Roo.BasicDialog} this
30762      */
30763     restoreState : function(){
30764         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30765         if(box && box.width){
30766             this.xy = [box.x, box.y];
30767             this.resizeTo(box.width, box.height);
30768         }
30769         return this;
30770     },
30771
30772     // private
30773     beforeShow : function(){
30774         this.expand();
30775         if(this.fixedcenter){
30776             this.xy = this.el.getCenterXY(true);
30777         }
30778         if(this.modal){
30779             Roo.get(document.body).addClass("x-body-masked");
30780             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30781             this.mask.show();
30782         }
30783         this.constrainXY();
30784     },
30785
30786     // private
30787     animShow : function(){
30788         var b = Roo.get(this.animateTarget).getBox();
30789         this.proxy.setSize(b.width, b.height);
30790         this.proxy.setLocation(b.x, b.y);
30791         this.proxy.show();
30792         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30793                     true, .35, this.showEl.createDelegate(this));
30794     },
30795
30796     /**
30797      * Shows the dialog.
30798      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30799      * @return {Roo.BasicDialog} this
30800      */
30801     show : function(animateTarget){
30802         if (this.fireEvent("beforeshow", this) === false){
30803             return;
30804         }
30805         if(this.syncHeightBeforeShow){
30806             this.syncBodyHeight();
30807         }else if(this.firstShow){
30808             this.firstShow = false;
30809             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30810         }
30811         this.animateTarget = animateTarget || this.animateTarget;
30812         if(!this.el.isVisible()){
30813             this.beforeShow();
30814             if(this.animateTarget && Roo.get(this.animateTarget)){
30815                 this.animShow();
30816             }else{
30817                 this.showEl();
30818             }
30819         }
30820         return this;
30821     },
30822
30823     // private
30824     showEl : function(){
30825         this.proxy.hide();
30826         this.el.setXY(this.xy);
30827         this.el.show();
30828         this.adjustAssets(true);
30829         this.toFront();
30830         this.focus();
30831         // IE peekaboo bug - fix found by Dave Fenwick
30832         if(Roo.isIE){
30833             this.el.repaint();
30834         }
30835         this.fireEvent("show", this);
30836     },
30837
30838     /**
30839      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30840      * dialog itself will receive focus.
30841      */
30842     focus : function(){
30843         if(this.defaultButton){
30844             this.defaultButton.focus();
30845         }else{
30846             this.focusEl.focus();
30847         }
30848     },
30849
30850     // private
30851     constrainXY : function(){
30852         if(this.constraintoviewport !== false){
30853             if(!this.viewSize){
30854                 if(this.container){
30855                     var s = this.container.getSize();
30856                     this.viewSize = [s.width, s.height];
30857                 }else{
30858                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30859                 }
30860             }
30861             var s = Roo.get(this.container||document).getScroll();
30862
30863             var x = this.xy[0], y = this.xy[1];
30864             var w = this.size.width, h = this.size.height;
30865             var vw = this.viewSize[0], vh = this.viewSize[1];
30866             // only move it if it needs it
30867             var moved = false;
30868             // first validate right/bottom
30869             if(x + w > vw+s.left){
30870                 x = vw - w;
30871                 moved = true;
30872             }
30873             if(y + h > vh+s.top){
30874                 y = vh - h;
30875                 moved = true;
30876             }
30877             // then make sure top/left isn't negative
30878             if(x < s.left){
30879                 x = s.left;
30880                 moved = true;
30881             }
30882             if(y < s.top){
30883                 y = s.top;
30884                 moved = true;
30885             }
30886             if(moved){
30887                 // cache xy
30888                 this.xy = [x, y];
30889                 if(this.isVisible()){
30890                     this.el.setLocation(x, y);
30891                     this.adjustAssets();
30892                 }
30893             }
30894         }
30895     },
30896
30897     // private
30898     onDrag : function(){
30899         if(!this.proxyDrag){
30900             this.xy = this.el.getXY();
30901             this.adjustAssets();
30902         }
30903     },
30904
30905     // private
30906     adjustAssets : function(doShow){
30907         var x = this.xy[0], y = this.xy[1];
30908         var w = this.size.width, h = this.size.height;
30909         if(doShow === true){
30910             if(this.shadow){
30911                 this.shadow.show(this.el);
30912             }
30913             if(this.shim){
30914                 this.shim.show();
30915             }
30916         }
30917         if(this.shadow && this.shadow.isVisible()){
30918             this.shadow.show(this.el);
30919         }
30920         if(this.shim && this.shim.isVisible()){
30921             this.shim.setBounds(x, y, w, h);
30922         }
30923     },
30924
30925     // private
30926     adjustViewport : function(w, h){
30927         if(!w || !h){
30928             w = Roo.lib.Dom.getViewWidth();
30929             h = Roo.lib.Dom.getViewHeight();
30930         }
30931         // cache the size
30932         this.viewSize = [w, h];
30933         if(this.modal && this.mask.isVisible()){
30934             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30935             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30936         }
30937         if(this.isVisible()){
30938             this.constrainXY();
30939         }
30940     },
30941
30942     /**
30943      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30944      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30945      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30946      */
30947     destroy : function(removeEl){
30948         if(this.isVisible()){
30949             this.animateTarget = null;
30950             this.hide();
30951         }
30952         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30953         if(this.tabs){
30954             this.tabs.destroy(removeEl);
30955         }
30956         Roo.destroy(
30957              this.shim,
30958              this.proxy,
30959              this.resizer,
30960              this.close,
30961              this.mask
30962         );
30963         if(this.dd){
30964             this.dd.unreg();
30965         }
30966         if(this.buttons){
30967            for(var i = 0, len = this.buttons.length; i < len; i++){
30968                this.buttons[i].destroy();
30969            }
30970         }
30971         this.el.removeAllListeners();
30972         if(removeEl === true){
30973             this.el.update("");
30974             this.el.remove();
30975         }
30976         Roo.DialogManager.unregister(this);
30977     },
30978
30979     // private
30980     startMove : function(){
30981         if(this.proxyDrag){
30982             this.proxy.show();
30983         }
30984         if(this.constraintoviewport !== false){
30985             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30986         }
30987     },
30988
30989     // private
30990     endMove : function(){
30991         if(!this.proxyDrag){
30992             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30993         }else{
30994             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30995             this.proxy.hide();
30996         }
30997         this.refreshSize();
30998         this.adjustAssets();
30999         this.focus();
31000         this.fireEvent("move", this, this.xy[0], this.xy[1]);
31001     },
31002
31003     /**
31004      * Brings this dialog to the front of any other visible dialogs
31005      * @return {Roo.BasicDialog} this
31006      */
31007     toFront : function(){
31008         Roo.DialogManager.bringToFront(this);
31009         return this;
31010     },
31011
31012     /**
31013      * Sends this dialog to the back (under) of any other visible dialogs
31014      * @return {Roo.BasicDialog} this
31015      */
31016     toBack : function(){
31017         Roo.DialogManager.sendToBack(this);
31018         return this;
31019     },
31020
31021     /**
31022      * Centers this dialog in the viewport
31023      * @return {Roo.BasicDialog} this
31024      */
31025     center : function(){
31026         var xy = this.el.getCenterXY(true);
31027         this.moveTo(xy[0], xy[1]);
31028         return this;
31029     },
31030
31031     /**
31032      * Moves the dialog's top-left corner to the specified point
31033      * @param {Number} x
31034      * @param {Number} y
31035      * @return {Roo.BasicDialog} this
31036      */
31037     moveTo : function(x, y){
31038         this.xy = [x,y];
31039         if(this.isVisible()){
31040             this.el.setXY(this.xy);
31041             this.adjustAssets();
31042         }
31043         return this;
31044     },
31045
31046     /**
31047      * Aligns the dialog to the specified element
31048      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31049      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31050      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31051      * @return {Roo.BasicDialog} this
31052      */
31053     alignTo : function(element, position, offsets){
31054         this.xy = this.el.getAlignToXY(element, position, offsets);
31055         if(this.isVisible()){
31056             this.el.setXY(this.xy);
31057             this.adjustAssets();
31058         }
31059         return this;
31060     },
31061
31062     /**
31063      * Anchors an element to another element and realigns it when the window is resized.
31064      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31065      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31066      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31067      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31068      * is a number, it is used as the buffer delay (defaults to 50ms).
31069      * @return {Roo.BasicDialog} this
31070      */
31071     anchorTo : function(el, alignment, offsets, monitorScroll){
31072         var action = function(){
31073             this.alignTo(el, alignment, offsets);
31074         };
31075         Roo.EventManager.onWindowResize(action, this);
31076         var tm = typeof monitorScroll;
31077         if(tm != 'undefined'){
31078             Roo.EventManager.on(window, 'scroll', action, this,
31079                 {buffer: tm == 'number' ? monitorScroll : 50});
31080         }
31081         action.call(this);
31082         return this;
31083     },
31084
31085     /**
31086      * Returns true if the dialog is visible
31087      * @return {Boolean}
31088      */
31089     isVisible : function(){
31090         return this.el.isVisible();
31091     },
31092
31093     // private
31094     animHide : function(callback){
31095         var b = Roo.get(this.animateTarget).getBox();
31096         this.proxy.show();
31097         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31098         this.el.hide();
31099         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31100                     this.hideEl.createDelegate(this, [callback]));
31101     },
31102
31103     /**
31104      * Hides the dialog.
31105      * @param {Function} callback (optional) Function to call when the dialog is hidden
31106      * @return {Roo.BasicDialog} this
31107      */
31108     hide : function(callback){
31109         if (this.fireEvent("beforehide", this) === false){
31110             return;
31111         }
31112         if(this.shadow){
31113             this.shadow.hide();
31114         }
31115         if(this.shim) {
31116           this.shim.hide();
31117         }
31118         // sometimes animateTarget seems to get set.. causing problems...
31119         // this just double checks..
31120         if(this.animateTarget && Roo.get(this.animateTarget)) {
31121            this.animHide(callback);
31122         }else{
31123             this.el.hide();
31124             this.hideEl(callback);
31125         }
31126         return this;
31127     },
31128
31129     // private
31130     hideEl : function(callback){
31131         this.proxy.hide();
31132         if(this.modal){
31133             this.mask.hide();
31134             Roo.get(document.body).removeClass("x-body-masked");
31135         }
31136         this.fireEvent("hide", this);
31137         if(typeof callback == "function"){
31138             callback();
31139         }
31140     },
31141
31142     // private
31143     hideAction : function(){
31144         this.setLeft("-10000px");
31145         this.setTop("-10000px");
31146         this.setStyle("visibility", "hidden");
31147     },
31148
31149     // private
31150     refreshSize : function(){
31151         this.size = this.el.getSize();
31152         this.xy = this.el.getXY();
31153         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31154     },
31155
31156     // private
31157     // z-index is managed by the DialogManager and may be overwritten at any time
31158     setZIndex : function(index){
31159         if(this.modal){
31160             this.mask.setStyle("z-index", index);
31161         }
31162         if(this.shim){
31163             this.shim.setStyle("z-index", ++index);
31164         }
31165         if(this.shadow){
31166             this.shadow.setZIndex(++index);
31167         }
31168         this.el.setStyle("z-index", ++index);
31169         if(this.proxy){
31170             this.proxy.setStyle("z-index", ++index);
31171         }
31172         if(this.resizer){
31173             this.resizer.proxy.setStyle("z-index", ++index);
31174         }
31175
31176         this.lastZIndex = index;
31177     },
31178
31179     /**
31180      * Returns the element for this dialog
31181      * @return {Roo.Element} The underlying dialog Element
31182      */
31183     getEl : function(){
31184         return this.el;
31185     }
31186 });
31187
31188 /**
31189  * @class Roo.DialogManager
31190  * Provides global access to BasicDialogs that have been created and
31191  * support for z-indexing (layering) multiple open dialogs.
31192  */
31193 Roo.DialogManager = function(){
31194     var list = {};
31195     var accessList = [];
31196     var front = null;
31197
31198     // private
31199     var sortDialogs = function(d1, d2){
31200         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31201     };
31202
31203     // private
31204     var orderDialogs = function(){
31205         accessList.sort(sortDialogs);
31206         var seed = Roo.DialogManager.zseed;
31207         for(var i = 0, len = accessList.length; i < len; i++){
31208             var dlg = accessList[i];
31209             if(dlg){
31210                 dlg.setZIndex(seed + (i*10));
31211             }
31212         }
31213     };
31214
31215     return {
31216         /**
31217          * The starting z-index for BasicDialogs (defaults to 9000)
31218          * @type Number The z-index value
31219          */
31220         zseed : 9000,
31221
31222         // private
31223         register : function(dlg){
31224             list[dlg.id] = dlg;
31225             accessList.push(dlg);
31226         },
31227
31228         // private
31229         unregister : function(dlg){
31230             delete list[dlg.id];
31231             var i=0;
31232             var len=0;
31233             if(!accessList.indexOf){
31234                 for(  i = 0, len = accessList.length; i < len; i++){
31235                     if(accessList[i] == dlg){
31236                         accessList.splice(i, 1);
31237                         return;
31238                     }
31239                 }
31240             }else{
31241                  i = accessList.indexOf(dlg);
31242                 if(i != -1){
31243                     accessList.splice(i, 1);
31244                 }
31245             }
31246         },
31247
31248         /**
31249          * Gets a registered dialog by id
31250          * @param {String/Object} id The id of the dialog or a dialog
31251          * @return {Roo.BasicDialog} this
31252          */
31253         get : function(id){
31254             return typeof id == "object" ? id : list[id];
31255         },
31256
31257         /**
31258          * Brings the specified dialog to the front
31259          * @param {String/Object} dlg The id of the dialog or a dialog
31260          * @return {Roo.BasicDialog} this
31261          */
31262         bringToFront : function(dlg){
31263             dlg = this.get(dlg);
31264             if(dlg != front){
31265                 front = dlg;
31266                 dlg._lastAccess = new Date().getTime();
31267                 orderDialogs();
31268             }
31269             return dlg;
31270         },
31271
31272         /**
31273          * Sends the specified dialog to the back
31274          * @param {String/Object} dlg The id of the dialog or a dialog
31275          * @return {Roo.BasicDialog} this
31276          */
31277         sendToBack : function(dlg){
31278             dlg = this.get(dlg);
31279             dlg._lastAccess = -(new Date().getTime());
31280             orderDialogs();
31281             return dlg;
31282         },
31283
31284         /**
31285          * Hides all dialogs
31286          */
31287         hideAll : function(){
31288             for(var id in list){
31289                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31290                     list[id].hide();
31291                 }
31292             }
31293         }
31294     };
31295 }();
31296
31297 /**
31298  * @class Roo.LayoutDialog
31299  * @extends Roo.BasicDialog
31300  * Dialog which provides adjustments for working with a layout in a Dialog.
31301  * Add your necessary layout config options to the dialog's config.<br>
31302  * Example usage (including a nested layout):
31303  * <pre><code>
31304 if(!dialog){
31305     dialog = new Roo.LayoutDialog("download-dlg", {
31306         modal: true,
31307         width:600,
31308         height:450,
31309         shadow:true,
31310         minWidth:500,
31311         minHeight:350,
31312         autoTabs:true,
31313         proxyDrag:true,
31314         // layout config merges with the dialog config
31315         center:{
31316             tabPosition: "top",
31317             alwaysShowTabs: true
31318         }
31319     });
31320     dialog.addKeyListener(27, dialog.hide, dialog);
31321     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31322     dialog.addButton("Build It!", this.getDownload, this);
31323
31324     // we can even add nested layouts
31325     var innerLayout = new Roo.BorderLayout("dl-inner", {
31326         east: {
31327             initialSize: 200,
31328             autoScroll:true,
31329             split:true
31330         },
31331         center: {
31332             autoScroll:true
31333         }
31334     });
31335     innerLayout.beginUpdate();
31336     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31337     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31338     innerLayout.endUpdate(true);
31339
31340     var layout = dialog.getLayout();
31341     layout.beginUpdate();
31342     layout.add("center", new Roo.ContentPanel("standard-panel",
31343                         {title: "Download the Source", fitToFrame:true}));
31344     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31345                {title: "Build your own roo.js"}));
31346     layout.getRegion("center").showPanel(sp);
31347     layout.endUpdate();
31348 }
31349 </code></pre>
31350     * @constructor
31351     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31352     * @param {Object} config configuration options
31353   */
31354 Roo.LayoutDialog = function(el, cfg){
31355     
31356     var config=  cfg;
31357     if (typeof(cfg) == 'undefined') {
31358         config = Roo.apply({}, el);
31359         // not sure why we use documentElement here.. - it should always be body.
31360         // IE7 borks horribly if we use documentElement.
31361         // webkit also does not like documentElement - it creates a body element...
31362         el = Roo.get( document.body || document.documentElement ).createChild();
31363         //config.autoCreate = true;
31364     }
31365     
31366     
31367     config.autoTabs = false;
31368     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31369     this.body.setStyle({overflow:"hidden", position:"relative"});
31370     this.layout = new Roo.BorderLayout(this.body.dom, config);
31371     this.layout.monitorWindowResize = false;
31372     this.el.addClass("x-dlg-auto-layout");
31373     // fix case when center region overwrites center function
31374     this.center = Roo.BasicDialog.prototype.center;
31375     this.on("show", this.layout.layout, this.layout, true);
31376     if (config.items) {
31377         var xitems = config.items;
31378         delete config.items;
31379         Roo.each(xitems, this.addxtype, this);
31380     }
31381     
31382     
31383 };
31384 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31385     /**
31386      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31387      * @deprecated
31388      */
31389     endUpdate : function(){
31390         this.layout.endUpdate();
31391     },
31392
31393     /**
31394      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31395      *  @deprecated
31396      */
31397     beginUpdate : function(){
31398         this.layout.beginUpdate();
31399     },
31400
31401     /**
31402      * Get the BorderLayout for this dialog
31403      * @return {Roo.BorderLayout}
31404      */
31405     getLayout : function(){
31406         return this.layout;
31407     },
31408
31409     showEl : function(){
31410         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31411         if(Roo.isIE7){
31412             this.layout.layout();
31413         }
31414     },
31415
31416     // private
31417     // Use the syncHeightBeforeShow config option to control this automatically
31418     syncBodyHeight : function(){
31419         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31420         if(this.layout){this.layout.layout();}
31421     },
31422     
31423       /**
31424      * Add an xtype element (actually adds to the layout.)
31425      * @return {Object} xdata xtype object data.
31426      */
31427     
31428     addxtype : function(c) {
31429         return this.layout.addxtype(c);
31430     }
31431 });/*
31432  * Based on:
31433  * Ext JS Library 1.1.1
31434  * Copyright(c) 2006-2007, Ext JS, LLC.
31435  *
31436  * Originally Released Under LGPL - original licence link has changed is not relivant.
31437  *
31438  * Fork - LGPL
31439  * <script type="text/javascript">
31440  */
31441  
31442 /**
31443  * @class Roo.MessageBox
31444  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31445  * Example usage:
31446  *<pre><code>
31447 // Basic alert:
31448 Roo.Msg.alert('Status', 'Changes saved successfully.');
31449
31450 // Prompt for user data:
31451 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31452     if (btn == 'ok'){
31453         // process text value...
31454     }
31455 });
31456
31457 // Show a dialog using config options:
31458 Roo.Msg.show({
31459    title:'Save Changes?',
31460    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31461    buttons: Roo.Msg.YESNOCANCEL,
31462    fn: processResult,
31463    animEl: 'elId'
31464 });
31465 </code></pre>
31466  * @singleton
31467  */
31468 Roo.MessageBox = function(){
31469     var dlg, opt, mask, waitTimer;
31470     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31471     var buttons, activeTextEl, bwidth;
31472
31473     // private
31474     var handleButton = function(button){
31475         dlg.hide();
31476         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31477     };
31478
31479     // private
31480     var handleHide = function(){
31481         if(opt && opt.cls){
31482             dlg.el.removeClass(opt.cls);
31483         }
31484         if(waitTimer){
31485             Roo.TaskMgr.stop(waitTimer);
31486             waitTimer = null;
31487         }
31488     };
31489
31490     // private
31491     var updateButtons = function(b){
31492         var width = 0;
31493         if(!b){
31494             buttons["ok"].hide();
31495             buttons["cancel"].hide();
31496             buttons["yes"].hide();
31497             buttons["no"].hide();
31498             dlg.footer.dom.style.display = 'none';
31499             return width;
31500         }
31501         dlg.footer.dom.style.display = '';
31502         for(var k in buttons){
31503             if(typeof buttons[k] != "function"){
31504                 if(b[k]){
31505                     buttons[k].show();
31506                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31507                     width += buttons[k].el.getWidth()+15;
31508                 }else{
31509                     buttons[k].hide();
31510                 }
31511             }
31512         }
31513         return width;
31514     };
31515
31516     // private
31517     var handleEsc = function(d, k, e){
31518         if(opt && opt.closable !== false){
31519             dlg.hide();
31520         }
31521         if(e){
31522             e.stopEvent();
31523         }
31524     };
31525
31526     return {
31527         /**
31528          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31529          * @return {Roo.BasicDialog} The BasicDialog element
31530          */
31531         getDialog : function(){
31532            if(!dlg){
31533                 dlg = new Roo.BasicDialog("x-msg-box", {
31534                     autoCreate : true,
31535                     shadow: true,
31536                     draggable: true,
31537                     resizable:false,
31538                     constraintoviewport:false,
31539                     fixedcenter:true,
31540                     collapsible : false,
31541                     shim:true,
31542                     modal: true,
31543                     width:400, height:100,
31544                     buttonAlign:"center",
31545                     closeClick : function(){
31546                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31547                             handleButton("no");
31548                         }else{
31549                             handleButton("cancel");
31550                         }
31551                     }
31552                 });
31553                 dlg.on("hide", handleHide);
31554                 mask = dlg.mask;
31555                 dlg.addKeyListener(27, handleEsc);
31556                 buttons = {};
31557                 var bt = this.buttonText;
31558                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31559                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31560                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31561                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31562                 bodyEl = dlg.body.createChild({
31563
31564                     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>'
31565                 });
31566                 msgEl = bodyEl.dom.firstChild;
31567                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31568                 textboxEl.enableDisplayMode();
31569                 textboxEl.addKeyListener([10,13], function(){
31570                     if(dlg.isVisible() && opt && opt.buttons){
31571                         if(opt.buttons.ok){
31572                             handleButton("ok");
31573                         }else if(opt.buttons.yes){
31574                             handleButton("yes");
31575                         }
31576                     }
31577                 });
31578                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31579                 textareaEl.enableDisplayMode();
31580                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31581                 progressEl.enableDisplayMode();
31582                 var pf = progressEl.dom.firstChild;
31583                 if (pf) {
31584                     pp = Roo.get(pf.firstChild);
31585                     pp.setHeight(pf.offsetHeight);
31586                 }
31587                 
31588             }
31589             return dlg;
31590         },
31591
31592         /**
31593          * Updates the message box body text
31594          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31595          * the XHTML-compliant non-breaking space character '&amp;#160;')
31596          * @return {Roo.MessageBox} This message box
31597          */
31598         updateText : function(text){
31599             if(!dlg.isVisible() && !opt.width){
31600                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31601             }
31602             msgEl.innerHTML = text || '&#160;';
31603       
31604             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31605             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31606             var w = Math.max(
31607                     Math.min(opt.width || cw , this.maxWidth), 
31608                     Math.max(opt.minWidth || this.minWidth, bwidth)
31609             );
31610             if(opt.prompt){
31611                 activeTextEl.setWidth(w);
31612             }
31613             if(dlg.isVisible()){
31614                 dlg.fixedcenter = false;
31615             }
31616             // to big, make it scroll. = But as usual stupid IE does not support
31617             // !important..
31618             
31619             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31620                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31621                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31622             } else {
31623                 bodyEl.dom.style.height = '';
31624                 bodyEl.dom.style.overflowY = '';
31625             }
31626             if (cw > w) {
31627                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31628             } else {
31629                 bodyEl.dom.style.overflowX = '';
31630             }
31631             
31632             dlg.setContentSize(w, bodyEl.getHeight());
31633             if(dlg.isVisible()){
31634                 dlg.fixedcenter = true;
31635             }
31636             return this;
31637         },
31638
31639         /**
31640          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31641          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31642          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31643          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31644          * @return {Roo.MessageBox} This message box
31645          */
31646         updateProgress : function(value, text){
31647             if(text){
31648                 this.updateText(text);
31649             }
31650             if (pp) { // weird bug on my firefox - for some reason this is not defined
31651                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31652             }
31653             return this;
31654         },        
31655
31656         /**
31657          * Returns true if the message box is currently displayed
31658          * @return {Boolean} True if the message box is visible, else false
31659          */
31660         isVisible : function(){
31661             return dlg && dlg.isVisible();  
31662         },
31663
31664         /**
31665          * Hides the message box if it is displayed
31666          */
31667         hide : function(){
31668             if(this.isVisible()){
31669                 dlg.hide();
31670             }  
31671         },
31672
31673         /**
31674          * Displays a new message box, or reinitializes an existing message box, based on the config options
31675          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31676          * The following config object properties are supported:
31677          * <pre>
31678 Property    Type             Description
31679 ----------  ---------------  ------------------------------------------------------------------------------------
31680 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31681                                    closes (defaults to undefined)
31682 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31683                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31684 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31685                                    progress and wait dialogs will ignore this property and always hide the
31686                                    close button as they can only be closed programmatically.
31687 cls               String           A custom CSS class to apply to the message box element
31688 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31689                                    displayed (defaults to 75)
31690 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31691                                    function will be btn (the name of the button that was clicked, if applicable,
31692                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31693                                    Progress and wait dialogs will ignore this option since they do not respond to
31694                                    user actions and can only be closed programmatically, so any required function
31695                                    should be called by the same code after it closes the dialog.
31696 icon              String           A CSS class that provides a background image to be used as an icon for
31697                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31698 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31699 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31700 modal             Boolean          False to allow user interaction with the page while the message box is
31701                                    displayed (defaults to true)
31702 msg               String           A string that will replace the existing message box body text (defaults
31703                                    to the XHTML-compliant non-breaking space character '&#160;')
31704 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31705 progress          Boolean          True to display a progress bar (defaults to false)
31706 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31707 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31708 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31709 title             String           The title text
31710 value             String           The string value to set into the active textbox element if displayed
31711 wait              Boolean          True to display a progress bar (defaults to false)
31712 width             Number           The width of the dialog in pixels
31713 </pre>
31714          *
31715          * Example usage:
31716          * <pre><code>
31717 Roo.Msg.show({
31718    title: 'Address',
31719    msg: 'Please enter your address:',
31720    width: 300,
31721    buttons: Roo.MessageBox.OKCANCEL,
31722    multiline: true,
31723    fn: saveAddress,
31724    animEl: 'addAddressBtn'
31725 });
31726 </code></pre>
31727          * @param {Object} config Configuration options
31728          * @return {Roo.MessageBox} This message box
31729          */
31730         show : function(options)
31731         {
31732             
31733             // this causes nightmares if you show one dialog after another
31734             // especially on callbacks..
31735              
31736             if(this.isVisible()){
31737                 
31738                 this.hide();
31739                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31740                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31741                 Roo.log("New Dialog Message:" +  options.msg )
31742                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31743                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31744                 
31745             }
31746             var d = this.getDialog();
31747             opt = options;
31748             d.setTitle(opt.title || "&#160;");
31749             d.close.setDisplayed(opt.closable !== false);
31750             activeTextEl = textboxEl;
31751             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31752             if(opt.prompt){
31753                 if(opt.multiline){
31754                     textboxEl.hide();
31755                     textareaEl.show();
31756                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31757                         opt.multiline : this.defaultTextHeight);
31758                     activeTextEl = textareaEl;
31759                 }else{
31760                     textboxEl.show();
31761                     textareaEl.hide();
31762                 }
31763             }else{
31764                 textboxEl.hide();
31765                 textareaEl.hide();
31766             }
31767             progressEl.setDisplayed(opt.progress === true);
31768             this.updateProgress(0);
31769             activeTextEl.dom.value = opt.value || "";
31770             if(opt.prompt){
31771                 dlg.setDefaultButton(activeTextEl);
31772             }else{
31773                 var bs = opt.buttons;
31774                 var db = null;
31775                 if(bs && bs.ok){
31776                     db = buttons["ok"];
31777                 }else if(bs && bs.yes){
31778                     db = buttons["yes"];
31779                 }
31780                 dlg.setDefaultButton(db);
31781             }
31782             bwidth = updateButtons(opt.buttons);
31783             this.updateText(opt.msg);
31784             if(opt.cls){
31785                 d.el.addClass(opt.cls);
31786             }
31787             d.proxyDrag = opt.proxyDrag === true;
31788             d.modal = opt.modal !== false;
31789             d.mask = opt.modal !== false ? mask : false;
31790             if(!d.isVisible()){
31791                 // force it to the end of the z-index stack so it gets a cursor in FF
31792                 document.body.appendChild(dlg.el.dom);
31793                 d.animateTarget = null;
31794                 d.show(options.animEl);
31795             }
31796             return this;
31797         },
31798
31799         /**
31800          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31801          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31802          * and closing the message box when the process is complete.
31803          * @param {String} title The title bar text
31804          * @param {String} msg The message box body text
31805          * @return {Roo.MessageBox} This message box
31806          */
31807         progress : function(title, msg){
31808             this.show({
31809                 title : title,
31810                 msg : msg,
31811                 buttons: false,
31812                 progress:true,
31813                 closable:false,
31814                 minWidth: this.minProgressWidth,
31815                 modal : true
31816             });
31817             return this;
31818         },
31819
31820         /**
31821          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31822          * If a callback function is passed it will be called after the user clicks the button, and the
31823          * id of the button that was clicked will be passed as the only parameter to the callback
31824          * (could also be the top-right close button).
31825          * @param {String} title The title bar text
31826          * @param {String} msg The message box body text
31827          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31828          * @param {Object} scope (optional) The scope of the callback function
31829          * @return {Roo.MessageBox} This message box
31830          */
31831         alert : function(title, msg, fn, scope){
31832             this.show({
31833                 title : title,
31834                 msg : msg,
31835                 buttons: this.OK,
31836                 fn: fn,
31837                 scope : scope,
31838                 modal : true
31839             });
31840             return this;
31841         },
31842
31843         /**
31844          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31845          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31846          * You are responsible for closing the message box when the process is complete.
31847          * @param {String} msg The message box body text
31848          * @param {String} title (optional) The title bar text
31849          * @return {Roo.MessageBox} This message box
31850          */
31851         wait : function(msg, title){
31852             this.show({
31853                 title : title,
31854                 msg : msg,
31855                 buttons: false,
31856                 closable:false,
31857                 progress:true,
31858                 modal:true,
31859                 width:300,
31860                 wait:true
31861             });
31862             waitTimer = Roo.TaskMgr.start({
31863                 run: function(i){
31864                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31865                 },
31866                 interval: 1000
31867             });
31868             return this;
31869         },
31870
31871         /**
31872          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31873          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31874          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31875          * @param {String} title The title bar text
31876          * @param {String} msg The message box body text
31877          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31878          * @param {Object} scope (optional) The scope of the callback function
31879          * @return {Roo.MessageBox} This message box
31880          */
31881         confirm : function(title, msg, fn, scope){
31882             this.show({
31883                 title : title,
31884                 msg : msg,
31885                 buttons: this.YESNO,
31886                 fn: fn,
31887                 scope : scope,
31888                 modal : true
31889             });
31890             return this;
31891         },
31892
31893         /**
31894          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31895          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31896          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31897          * (could also be the top-right close button) and the text that was entered will be passed as the two
31898          * parameters to the callback.
31899          * @param {String} title The title bar text
31900          * @param {String} msg The message box body text
31901          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31902          * @param {Object} scope (optional) The scope of the callback function
31903          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31904          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31905          * @return {Roo.MessageBox} This message box
31906          */
31907         prompt : function(title, msg, fn, scope, multiline){
31908             this.show({
31909                 title : title,
31910                 msg : msg,
31911                 buttons: this.OKCANCEL,
31912                 fn: fn,
31913                 minWidth:250,
31914                 scope : scope,
31915                 prompt:true,
31916                 multiline: multiline,
31917                 modal : true
31918             });
31919             return this;
31920         },
31921
31922         /**
31923          * Button config that displays a single OK button
31924          * @type Object
31925          */
31926         OK : {ok:true},
31927         /**
31928          * Button config that displays Yes and No buttons
31929          * @type Object
31930          */
31931         YESNO : {yes:true, no:true},
31932         /**
31933          * Button config that displays OK and Cancel buttons
31934          * @type Object
31935          */
31936         OKCANCEL : {ok:true, cancel:true},
31937         /**
31938          * Button config that displays Yes, No and Cancel buttons
31939          * @type Object
31940          */
31941         YESNOCANCEL : {yes:true, no:true, cancel:true},
31942
31943         /**
31944          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31945          * @type Number
31946          */
31947         defaultTextHeight : 75,
31948         /**
31949          * The maximum width in pixels of the message box (defaults to 600)
31950          * @type Number
31951          */
31952         maxWidth : 600,
31953         /**
31954          * The minimum width in pixels of the message box (defaults to 100)
31955          * @type Number
31956          */
31957         minWidth : 100,
31958         /**
31959          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31960          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31961          * @type Number
31962          */
31963         minProgressWidth : 250,
31964         /**
31965          * An object containing the default button text strings that can be overriden for localized language support.
31966          * Supported properties are: ok, cancel, yes and no.
31967          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31968          * @type Object
31969          */
31970         buttonText : {
31971             ok : "OK",
31972             cancel : "Cancel",
31973             yes : "Yes",
31974             no : "No"
31975         }
31976     };
31977 }();
31978
31979 /**
31980  * Shorthand for {@link Roo.MessageBox}
31981  */
31982 Roo.Msg = Roo.MessageBox;/*
31983  * Based on:
31984  * Ext JS Library 1.1.1
31985  * Copyright(c) 2006-2007, Ext JS, LLC.
31986  *
31987  * Originally Released Under LGPL - original licence link has changed is not relivant.
31988  *
31989  * Fork - LGPL
31990  * <script type="text/javascript">
31991  */
31992 /**
31993  * @class Roo.QuickTips
31994  * Provides attractive and customizable tooltips for any element.
31995  * @singleton
31996  */
31997 Roo.QuickTips = function(){
31998     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31999     var ce, bd, xy, dd;
32000     var visible = false, disabled = true, inited = false;
32001     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
32002     
32003     var onOver = function(e){
32004         if(disabled){
32005             return;
32006         }
32007         var t = e.getTarget();
32008         if(!t || t.nodeType !== 1 || t == document || t == document.body){
32009             return;
32010         }
32011         if(ce && t == ce.el){
32012             clearTimeout(hideProc);
32013             return;
32014         }
32015         if(t && tagEls[t.id]){
32016             tagEls[t.id].el = t;
32017             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32018             return;
32019         }
32020         var ttp, et = Roo.fly(t);
32021         var ns = cfg.namespace;
32022         if(tm.interceptTitles && t.title){
32023             ttp = t.title;
32024             t.qtip = ttp;
32025             t.removeAttribute("title");
32026             e.preventDefault();
32027         }else{
32028             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32029         }
32030         if(ttp){
32031             showProc = show.defer(tm.showDelay, tm, [{
32032                 el: t, 
32033                 text: ttp, 
32034                 width: et.getAttributeNS(ns, cfg.width),
32035                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32036                 title: et.getAttributeNS(ns, cfg.title),
32037                     cls: et.getAttributeNS(ns, cfg.cls)
32038             }]);
32039         }
32040     };
32041     
32042     var onOut = function(e){
32043         clearTimeout(showProc);
32044         var t = e.getTarget();
32045         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32046             hideProc = setTimeout(hide, tm.hideDelay);
32047         }
32048     };
32049     
32050     var onMove = function(e){
32051         if(disabled){
32052             return;
32053         }
32054         xy = e.getXY();
32055         xy[1] += 18;
32056         if(tm.trackMouse && ce){
32057             el.setXY(xy);
32058         }
32059     };
32060     
32061     var onDown = function(e){
32062         clearTimeout(showProc);
32063         clearTimeout(hideProc);
32064         if(!e.within(el)){
32065             if(tm.hideOnClick){
32066                 hide();
32067                 tm.disable();
32068                 tm.enable.defer(100, tm);
32069             }
32070         }
32071     };
32072     
32073     var getPad = function(){
32074         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32075     };
32076
32077     var show = function(o){
32078         if(disabled){
32079             return;
32080         }
32081         clearTimeout(dismissProc);
32082         ce = o;
32083         if(removeCls){ // in case manually hidden
32084             el.removeClass(removeCls);
32085             removeCls = null;
32086         }
32087         if(ce.cls){
32088             el.addClass(ce.cls);
32089             removeCls = ce.cls;
32090         }
32091         if(ce.title){
32092             tipTitle.update(ce.title);
32093             tipTitle.show();
32094         }else{
32095             tipTitle.update('');
32096             tipTitle.hide();
32097         }
32098         el.dom.style.width  = tm.maxWidth+'px';
32099         //tipBody.dom.style.width = '';
32100         tipBodyText.update(o.text);
32101         var p = getPad(), w = ce.width;
32102         if(!w){
32103             var td = tipBodyText.dom;
32104             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32105             if(aw > tm.maxWidth){
32106                 w = tm.maxWidth;
32107             }else if(aw < tm.minWidth){
32108                 w = tm.minWidth;
32109             }else{
32110                 w = aw;
32111             }
32112         }
32113         //tipBody.setWidth(w);
32114         el.setWidth(parseInt(w, 10) + p);
32115         if(ce.autoHide === false){
32116             close.setDisplayed(true);
32117             if(dd){
32118                 dd.unlock();
32119             }
32120         }else{
32121             close.setDisplayed(false);
32122             if(dd){
32123                 dd.lock();
32124             }
32125         }
32126         if(xy){
32127             el.avoidY = xy[1]-18;
32128             el.setXY(xy);
32129         }
32130         if(tm.animate){
32131             el.setOpacity(.1);
32132             el.setStyle("visibility", "visible");
32133             el.fadeIn({callback: afterShow});
32134         }else{
32135             afterShow();
32136         }
32137     };
32138     
32139     var afterShow = function(){
32140         if(ce){
32141             el.show();
32142             esc.enable();
32143             if(tm.autoDismiss && ce.autoHide !== false){
32144                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32145             }
32146         }
32147     };
32148     
32149     var hide = function(noanim){
32150         clearTimeout(dismissProc);
32151         clearTimeout(hideProc);
32152         ce = null;
32153         if(el.isVisible()){
32154             esc.disable();
32155             if(noanim !== true && tm.animate){
32156                 el.fadeOut({callback: afterHide});
32157             }else{
32158                 afterHide();
32159             } 
32160         }
32161     };
32162     
32163     var afterHide = function(){
32164         el.hide();
32165         if(removeCls){
32166             el.removeClass(removeCls);
32167             removeCls = null;
32168         }
32169     };
32170     
32171     return {
32172         /**
32173         * @cfg {Number} minWidth
32174         * The minimum width of the quick tip (defaults to 40)
32175         */
32176        minWidth : 40,
32177         /**
32178         * @cfg {Number} maxWidth
32179         * The maximum width of the quick tip (defaults to 300)
32180         */
32181        maxWidth : 300,
32182         /**
32183         * @cfg {Boolean} interceptTitles
32184         * True to automatically use the element's DOM title value if available (defaults to false)
32185         */
32186        interceptTitles : false,
32187         /**
32188         * @cfg {Boolean} trackMouse
32189         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32190         */
32191        trackMouse : false,
32192         /**
32193         * @cfg {Boolean} hideOnClick
32194         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32195         */
32196        hideOnClick : true,
32197         /**
32198         * @cfg {Number} showDelay
32199         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32200         */
32201        showDelay : 500,
32202         /**
32203         * @cfg {Number} hideDelay
32204         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32205         */
32206        hideDelay : 200,
32207         /**
32208         * @cfg {Boolean} autoHide
32209         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32210         * Used in conjunction with hideDelay.
32211         */
32212        autoHide : true,
32213         /**
32214         * @cfg {Boolean}
32215         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32216         * (defaults to true).  Used in conjunction with autoDismissDelay.
32217         */
32218        autoDismiss : true,
32219         /**
32220         * @cfg {Number}
32221         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32222         */
32223        autoDismissDelay : 5000,
32224        /**
32225         * @cfg {Boolean} animate
32226         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32227         */
32228        animate : false,
32229
32230        /**
32231         * @cfg {String} title
32232         * Title text to display (defaults to '').  This can be any valid HTML markup.
32233         */
32234         title: '',
32235        /**
32236         * @cfg {String} text
32237         * Body text to display (defaults to '').  This can be any valid HTML markup.
32238         */
32239         text : '',
32240        /**
32241         * @cfg {String} cls
32242         * A CSS class to apply to the base quick tip element (defaults to '').
32243         */
32244         cls : '',
32245        /**
32246         * @cfg {Number} width
32247         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32248         * minWidth or maxWidth.
32249         */
32250         width : null,
32251
32252     /**
32253      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32254      * or display QuickTips in a page.
32255      */
32256        init : function(){
32257           tm = Roo.QuickTips;
32258           cfg = tm.tagConfig;
32259           if(!inited){
32260               if(!Roo.isReady){ // allow calling of init() before onReady
32261                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32262                   return;
32263               }
32264               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32265               el.fxDefaults = {stopFx: true};
32266               // maximum custom styling
32267               //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>');
32268               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>');              
32269               tipTitle = el.child('h3');
32270               tipTitle.enableDisplayMode("block");
32271               tipBody = el.child('div.x-tip-bd');
32272               tipBodyText = el.child('div.x-tip-bd-inner');
32273               //bdLeft = el.child('div.x-tip-bd-left');
32274               //bdRight = el.child('div.x-tip-bd-right');
32275               close = el.child('div.x-tip-close');
32276               close.enableDisplayMode("block");
32277               close.on("click", hide);
32278               var d = Roo.get(document);
32279               d.on("mousedown", onDown);
32280               d.on("mouseover", onOver);
32281               d.on("mouseout", onOut);
32282               d.on("mousemove", onMove);
32283               esc = d.addKeyListener(27, hide);
32284               esc.disable();
32285               if(Roo.dd.DD){
32286                   dd = el.initDD("default", null, {
32287                       onDrag : function(){
32288                           el.sync();  
32289                       }
32290                   });
32291                   dd.setHandleElId(tipTitle.id);
32292                   dd.lock();
32293               }
32294               inited = true;
32295           }
32296           this.enable(); 
32297        },
32298
32299     /**
32300      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32301      * are supported:
32302      * <pre>
32303 Property    Type                   Description
32304 ----------  ---------------------  ------------------------------------------------------------------------
32305 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32306      * </ul>
32307      * @param {Object} config The config object
32308      */
32309        register : function(config){
32310            var cs = config instanceof Array ? config : arguments;
32311            for(var i = 0, len = cs.length; i < len; i++) {
32312                var c = cs[i];
32313                var target = c.target;
32314                if(target){
32315                    if(target instanceof Array){
32316                        for(var j = 0, jlen = target.length; j < jlen; j++){
32317                            tagEls[target[j]] = c;
32318                        }
32319                    }else{
32320                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32321                    }
32322                }
32323            }
32324        },
32325
32326     /**
32327      * Removes this quick tip from its element and destroys it.
32328      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32329      */
32330        unregister : function(el){
32331            delete tagEls[Roo.id(el)];
32332        },
32333
32334     /**
32335      * Enable this quick tip.
32336      */
32337        enable : function(){
32338            if(inited && disabled){
32339                locks.pop();
32340                if(locks.length < 1){
32341                    disabled = false;
32342                }
32343            }
32344        },
32345
32346     /**
32347      * Disable this quick tip.
32348      */
32349        disable : function(){
32350           disabled = true;
32351           clearTimeout(showProc);
32352           clearTimeout(hideProc);
32353           clearTimeout(dismissProc);
32354           if(ce){
32355               hide(true);
32356           }
32357           locks.push(1);
32358        },
32359
32360     /**
32361      * Returns true if the quick tip is enabled, else false.
32362      */
32363        isEnabled : function(){
32364             return !disabled;
32365        },
32366
32367         // private
32368        tagConfig : {
32369            namespace : "ext",
32370            attribute : "qtip",
32371            width : "width",
32372            target : "target",
32373            title : "qtitle",
32374            hide : "hide",
32375            cls : "qclass"
32376        }
32377    };
32378 }();
32379
32380 // backwards compat
32381 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32382  * Based on:
32383  * Ext JS Library 1.1.1
32384  * Copyright(c) 2006-2007, Ext JS, LLC.
32385  *
32386  * Originally Released Under LGPL - original licence link has changed is not relivant.
32387  *
32388  * Fork - LGPL
32389  * <script type="text/javascript">
32390  */
32391  
32392
32393 /**
32394  * @class Roo.tree.TreePanel
32395  * @extends Roo.data.Tree
32396
32397  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32398  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32399  * @cfg {Boolean} enableDD true to enable drag and drop
32400  * @cfg {Boolean} enableDrag true to enable just drag
32401  * @cfg {Boolean} enableDrop true to enable just drop
32402  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32403  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32404  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32405  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32406  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32407  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32408  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32409  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32410  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32411  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32412  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32413  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32414  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32415  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32416  * @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>
32417  * @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>
32418  * 
32419  * @constructor
32420  * @param {String/HTMLElement/Element} el The container element
32421  * @param {Object} config
32422  */
32423 Roo.tree.TreePanel = function(el, config){
32424     var root = false;
32425     var loader = false;
32426     if (config.root) {
32427         root = config.root;
32428         delete config.root;
32429     }
32430     if (config.loader) {
32431         loader = config.loader;
32432         delete config.loader;
32433     }
32434     
32435     Roo.apply(this, config);
32436     Roo.tree.TreePanel.superclass.constructor.call(this);
32437     this.el = Roo.get(el);
32438     this.el.addClass('x-tree');
32439     //console.log(root);
32440     if (root) {
32441         this.setRootNode( Roo.factory(root, Roo.tree));
32442     }
32443     if (loader) {
32444         this.loader = Roo.factory(loader, Roo.tree);
32445     }
32446    /**
32447     * Read-only. The id of the container element becomes this TreePanel's id.
32448     */
32449     this.id = this.el.id;
32450     this.addEvents({
32451         /**
32452         * @event beforeload
32453         * Fires before a node is loaded, return false to cancel
32454         * @param {Node} node The node being loaded
32455         */
32456         "beforeload" : true,
32457         /**
32458         * @event load
32459         * Fires when a node is loaded
32460         * @param {Node} node The node that was loaded
32461         */
32462         "load" : true,
32463         /**
32464         * @event textchange
32465         * Fires when the text for a node is changed
32466         * @param {Node} node The node
32467         * @param {String} text The new text
32468         * @param {String} oldText The old text
32469         */
32470         "textchange" : true,
32471         /**
32472         * @event beforeexpand
32473         * Fires before a node is expanded, return false to cancel.
32474         * @param {Node} node The node
32475         * @param {Boolean} deep
32476         * @param {Boolean} anim
32477         */
32478         "beforeexpand" : true,
32479         /**
32480         * @event beforecollapse
32481         * Fires before a node is collapsed, return false to cancel.
32482         * @param {Node} node The node
32483         * @param {Boolean} deep
32484         * @param {Boolean} anim
32485         */
32486         "beforecollapse" : true,
32487         /**
32488         * @event expand
32489         * Fires when a node is expanded
32490         * @param {Node} node The node
32491         */
32492         "expand" : true,
32493         /**
32494         * @event disabledchange
32495         * Fires when the disabled status of a node changes
32496         * @param {Node} node The node
32497         * @param {Boolean} disabled
32498         */
32499         "disabledchange" : true,
32500         /**
32501         * @event collapse
32502         * Fires when a node is collapsed
32503         * @param {Node} node The node
32504         */
32505         "collapse" : true,
32506         /**
32507         * @event beforeclick
32508         * Fires before click processing on a node. Return false to cancel the default action.
32509         * @param {Node} node The node
32510         * @param {Roo.EventObject} e The event object
32511         */
32512         "beforeclick":true,
32513         /**
32514         * @event checkchange
32515         * Fires when a node with a checkbox's checked property changes
32516         * @param {Node} this This node
32517         * @param {Boolean} checked
32518         */
32519         "checkchange":true,
32520         /**
32521         * @event click
32522         * Fires when a node is clicked
32523         * @param {Node} node The node
32524         * @param {Roo.EventObject} e The event object
32525         */
32526         "click":true,
32527         /**
32528         * @event dblclick
32529         * Fires when a node is double clicked
32530         * @param {Node} node The node
32531         * @param {Roo.EventObject} e The event object
32532         */
32533         "dblclick":true,
32534         /**
32535         * @event contextmenu
32536         * Fires when a node is right clicked
32537         * @param {Node} node The node
32538         * @param {Roo.EventObject} e The event object
32539         */
32540         "contextmenu":true,
32541         /**
32542         * @event beforechildrenrendered
32543         * Fires right before the child nodes for a node are rendered
32544         * @param {Node} node The node
32545         */
32546         "beforechildrenrendered":true,
32547         /**
32548         * @event startdrag
32549         * Fires when a node starts being dragged
32550         * @param {Roo.tree.TreePanel} this
32551         * @param {Roo.tree.TreeNode} node
32552         * @param {event} e The raw browser event
32553         */ 
32554        "startdrag" : true,
32555        /**
32556         * @event enddrag
32557         * Fires when a drag operation is complete
32558         * @param {Roo.tree.TreePanel} this
32559         * @param {Roo.tree.TreeNode} node
32560         * @param {event} e The raw browser event
32561         */
32562        "enddrag" : true,
32563        /**
32564         * @event dragdrop
32565         * Fires when a dragged node is dropped on a valid DD target
32566         * @param {Roo.tree.TreePanel} this
32567         * @param {Roo.tree.TreeNode} node
32568         * @param {DD} dd The dd it was dropped on
32569         * @param {event} e The raw browser event
32570         */
32571        "dragdrop" : true,
32572        /**
32573         * @event beforenodedrop
32574         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32575         * passed to handlers has the following properties:<br />
32576         * <ul style="padding:5px;padding-left:16px;">
32577         * <li>tree - The TreePanel</li>
32578         * <li>target - The node being targeted for the drop</li>
32579         * <li>data - The drag data from the drag source</li>
32580         * <li>point - The point of the drop - append, above or below</li>
32581         * <li>source - The drag source</li>
32582         * <li>rawEvent - Raw mouse event</li>
32583         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32584         * to be inserted by setting them on this object.</li>
32585         * <li>cancel - Set this to true to cancel the drop.</li>
32586         * </ul>
32587         * @param {Object} dropEvent
32588         */
32589        "beforenodedrop" : true,
32590        /**
32591         * @event nodedrop
32592         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32593         * passed to handlers has the following properties:<br />
32594         * <ul style="padding:5px;padding-left:16px;">
32595         * <li>tree - The TreePanel</li>
32596         * <li>target - The node being targeted for the drop</li>
32597         * <li>data - The drag data from the drag source</li>
32598         * <li>point - The point of the drop - append, above or below</li>
32599         * <li>source - The drag source</li>
32600         * <li>rawEvent - Raw mouse event</li>
32601         * <li>dropNode - Dropped node(s).</li>
32602         * </ul>
32603         * @param {Object} dropEvent
32604         */
32605        "nodedrop" : true,
32606         /**
32607         * @event nodedragover
32608         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32609         * passed to handlers has the following properties:<br />
32610         * <ul style="padding:5px;padding-left:16px;">
32611         * <li>tree - The TreePanel</li>
32612         * <li>target - The node being targeted for the drop</li>
32613         * <li>data - The drag data from the drag source</li>
32614         * <li>point - The point of the drop - append, above or below</li>
32615         * <li>source - The drag source</li>
32616         * <li>rawEvent - Raw mouse event</li>
32617         * <li>dropNode - Drop node(s) provided by the source.</li>
32618         * <li>cancel - Set this to true to signal drop not allowed.</li>
32619         * </ul>
32620         * @param {Object} dragOverEvent
32621         */
32622        "nodedragover" : true
32623         
32624     });
32625     if(this.singleExpand){
32626        this.on("beforeexpand", this.restrictExpand, this);
32627     }
32628     if (this.editor) {
32629         this.editor.tree = this;
32630         this.editor = Roo.factory(this.editor, Roo.tree);
32631     }
32632     
32633     if (this.selModel) {
32634         this.selModel = Roo.factory(this.selModel, Roo.tree);
32635     }
32636    
32637 };
32638 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32639     rootVisible : true,
32640     animate: Roo.enableFx,
32641     lines : true,
32642     enableDD : false,
32643     hlDrop : Roo.enableFx,
32644   
32645     renderer: false,
32646     
32647     rendererTip: false,
32648     // private
32649     restrictExpand : function(node){
32650         var p = node.parentNode;
32651         if(p){
32652             if(p.expandedChild && p.expandedChild.parentNode == p){
32653                 p.expandedChild.collapse();
32654             }
32655             p.expandedChild = node;
32656         }
32657     },
32658
32659     // private override
32660     setRootNode : function(node){
32661         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32662         if(!this.rootVisible){
32663             node.ui = new Roo.tree.RootTreeNodeUI(node);
32664         }
32665         return node;
32666     },
32667
32668     /**
32669      * Returns the container element for this TreePanel
32670      */
32671     getEl : function(){
32672         return this.el;
32673     },
32674
32675     /**
32676      * Returns the default TreeLoader for this TreePanel
32677      */
32678     getLoader : function(){
32679         return this.loader;
32680     },
32681
32682     /**
32683      * Expand all nodes
32684      */
32685     expandAll : function(){
32686         this.root.expand(true);
32687     },
32688
32689     /**
32690      * Collapse all nodes
32691      */
32692     collapseAll : function(){
32693         this.root.collapse(true);
32694     },
32695
32696     /**
32697      * Returns the selection model used by this TreePanel
32698      */
32699     getSelectionModel : function(){
32700         if(!this.selModel){
32701             this.selModel = new Roo.tree.DefaultSelectionModel();
32702         }
32703         return this.selModel;
32704     },
32705
32706     /**
32707      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32708      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32709      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32710      * @return {Array}
32711      */
32712     getChecked : function(a, startNode){
32713         startNode = startNode || this.root;
32714         var r = [];
32715         var f = function(){
32716             if(this.attributes.checked){
32717                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32718             }
32719         }
32720         startNode.cascade(f);
32721         return r;
32722     },
32723
32724     /**
32725      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32726      * @param {String} path
32727      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32728      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32729      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32730      */
32731     expandPath : function(path, attr, callback){
32732         attr = attr || "id";
32733         var keys = path.split(this.pathSeparator);
32734         var curNode = this.root;
32735         if(curNode.attributes[attr] != keys[1]){ // invalid root
32736             if(callback){
32737                 callback(false, null);
32738             }
32739             return;
32740         }
32741         var index = 1;
32742         var f = function(){
32743             if(++index == keys.length){
32744                 if(callback){
32745                     callback(true, curNode);
32746                 }
32747                 return;
32748             }
32749             var c = curNode.findChild(attr, keys[index]);
32750             if(!c){
32751                 if(callback){
32752                     callback(false, curNode);
32753                 }
32754                 return;
32755             }
32756             curNode = c;
32757             c.expand(false, false, f);
32758         };
32759         curNode.expand(false, false, f);
32760     },
32761
32762     /**
32763      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32764      * @param {String} path
32765      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32766      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32767      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32768      */
32769     selectPath : function(path, attr, callback){
32770         attr = attr || "id";
32771         var keys = path.split(this.pathSeparator);
32772         var v = keys.pop();
32773         if(keys.length > 0){
32774             var f = function(success, node){
32775                 if(success && node){
32776                     var n = node.findChild(attr, v);
32777                     if(n){
32778                         n.select();
32779                         if(callback){
32780                             callback(true, n);
32781                         }
32782                     }else if(callback){
32783                         callback(false, n);
32784                     }
32785                 }else{
32786                     if(callback){
32787                         callback(false, n);
32788                     }
32789                 }
32790             };
32791             this.expandPath(keys.join(this.pathSeparator), attr, f);
32792         }else{
32793             this.root.select();
32794             if(callback){
32795                 callback(true, this.root);
32796             }
32797         }
32798     },
32799
32800     getTreeEl : function(){
32801         return this.el;
32802     },
32803
32804     /**
32805      * Trigger rendering of this TreePanel
32806      */
32807     render : function(){
32808         if (this.innerCt) {
32809             return this; // stop it rendering more than once!!
32810         }
32811         
32812         this.innerCt = this.el.createChild({tag:"ul",
32813                cls:"x-tree-root-ct " +
32814                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32815
32816         if(this.containerScroll){
32817             Roo.dd.ScrollManager.register(this.el);
32818         }
32819         if((this.enableDD || this.enableDrop) && !this.dropZone){
32820            /**
32821             * The dropZone used by this tree if drop is enabled
32822             * @type Roo.tree.TreeDropZone
32823             */
32824              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32825                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32826            });
32827         }
32828         if((this.enableDD || this.enableDrag) && !this.dragZone){
32829            /**
32830             * The dragZone used by this tree if drag is enabled
32831             * @type Roo.tree.TreeDragZone
32832             */
32833             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32834                ddGroup: this.ddGroup || "TreeDD",
32835                scroll: this.ddScroll
32836            });
32837         }
32838         this.getSelectionModel().init(this);
32839         if (!this.root) {
32840             Roo.log("ROOT not set in tree");
32841             return this;
32842         }
32843         this.root.render();
32844         if(!this.rootVisible){
32845             this.root.renderChildren();
32846         }
32847         return this;
32848     }
32849 });/*
32850  * Based on:
32851  * Ext JS Library 1.1.1
32852  * Copyright(c) 2006-2007, Ext JS, LLC.
32853  *
32854  * Originally Released Under LGPL - original licence link has changed is not relivant.
32855  *
32856  * Fork - LGPL
32857  * <script type="text/javascript">
32858  */
32859  
32860
32861 /**
32862  * @class Roo.tree.DefaultSelectionModel
32863  * @extends Roo.util.Observable
32864  * The default single selection for a TreePanel.
32865  * @param {Object} cfg Configuration
32866  */
32867 Roo.tree.DefaultSelectionModel = function(cfg){
32868    this.selNode = null;
32869    
32870    
32871    
32872    this.addEvents({
32873        /**
32874         * @event selectionchange
32875         * Fires when the selected node changes
32876         * @param {DefaultSelectionModel} this
32877         * @param {TreeNode} node the new selection
32878         */
32879        "selectionchange" : true,
32880
32881        /**
32882         * @event beforeselect
32883         * Fires before the selected node changes, return false to cancel the change
32884         * @param {DefaultSelectionModel} this
32885         * @param {TreeNode} node the new selection
32886         * @param {TreeNode} node the old selection
32887         */
32888        "beforeselect" : true
32889    });
32890    
32891     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32892 };
32893
32894 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32895     init : function(tree){
32896         this.tree = tree;
32897         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32898         tree.on("click", this.onNodeClick, this);
32899     },
32900     
32901     onNodeClick : function(node, e){
32902         if (e.ctrlKey && this.selNode == node)  {
32903             this.unselect(node);
32904             return;
32905         }
32906         this.select(node);
32907     },
32908     
32909     /**
32910      * Select a node.
32911      * @param {TreeNode} node The node to select
32912      * @return {TreeNode} The selected node
32913      */
32914     select : function(node){
32915         var last = this.selNode;
32916         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32917             if(last){
32918                 last.ui.onSelectedChange(false);
32919             }
32920             this.selNode = node;
32921             node.ui.onSelectedChange(true);
32922             this.fireEvent("selectionchange", this, node, last);
32923         }
32924         return node;
32925     },
32926     
32927     /**
32928      * Deselect a node.
32929      * @param {TreeNode} node The node to unselect
32930      */
32931     unselect : function(node){
32932         if(this.selNode == node){
32933             this.clearSelections();
32934         }    
32935     },
32936     
32937     /**
32938      * Clear all selections
32939      */
32940     clearSelections : function(){
32941         var n = this.selNode;
32942         if(n){
32943             n.ui.onSelectedChange(false);
32944             this.selNode = null;
32945             this.fireEvent("selectionchange", this, null);
32946         }
32947         return n;
32948     },
32949     
32950     /**
32951      * Get the selected node
32952      * @return {TreeNode} The selected node
32953      */
32954     getSelectedNode : function(){
32955         return this.selNode;    
32956     },
32957     
32958     /**
32959      * Returns true if the node is selected
32960      * @param {TreeNode} node The node to check
32961      * @return {Boolean}
32962      */
32963     isSelected : function(node){
32964         return this.selNode == node;  
32965     },
32966
32967     /**
32968      * Selects the node above the selected node in the tree, intelligently walking the nodes
32969      * @return TreeNode The new selection
32970      */
32971     selectPrevious : function(){
32972         var s = this.selNode || this.lastSelNode;
32973         if(!s){
32974             return null;
32975         }
32976         var ps = s.previousSibling;
32977         if(ps){
32978             if(!ps.isExpanded() || ps.childNodes.length < 1){
32979                 return this.select(ps);
32980             } else{
32981                 var lc = ps.lastChild;
32982                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32983                     lc = lc.lastChild;
32984                 }
32985                 return this.select(lc);
32986             }
32987         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32988             return this.select(s.parentNode);
32989         }
32990         return null;
32991     },
32992
32993     /**
32994      * Selects the node above the selected node in the tree, intelligently walking the nodes
32995      * @return TreeNode The new selection
32996      */
32997     selectNext : function(){
32998         var s = this.selNode || this.lastSelNode;
32999         if(!s){
33000             return null;
33001         }
33002         if(s.firstChild && s.isExpanded()){
33003              return this.select(s.firstChild);
33004          }else if(s.nextSibling){
33005              return this.select(s.nextSibling);
33006          }else if(s.parentNode){
33007             var newS = null;
33008             s.parentNode.bubble(function(){
33009                 if(this.nextSibling){
33010                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33011                     return false;
33012                 }
33013             });
33014             return newS;
33015          }
33016         return null;
33017     },
33018
33019     onKeyDown : function(e){
33020         var s = this.selNode || this.lastSelNode;
33021         // undesirable, but required
33022         var sm = this;
33023         if(!s){
33024             return;
33025         }
33026         var k = e.getKey();
33027         switch(k){
33028              case e.DOWN:
33029                  e.stopEvent();
33030                  this.selectNext();
33031              break;
33032              case e.UP:
33033                  e.stopEvent();
33034                  this.selectPrevious();
33035              break;
33036              case e.RIGHT:
33037                  e.preventDefault();
33038                  if(s.hasChildNodes()){
33039                      if(!s.isExpanded()){
33040                          s.expand();
33041                      }else if(s.firstChild){
33042                          this.select(s.firstChild, e);
33043                      }
33044                  }
33045              break;
33046              case e.LEFT:
33047                  e.preventDefault();
33048                  if(s.hasChildNodes() && s.isExpanded()){
33049                      s.collapse();
33050                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33051                      this.select(s.parentNode, e);
33052                  }
33053              break;
33054         };
33055     }
33056 });
33057
33058 /**
33059  * @class Roo.tree.MultiSelectionModel
33060  * @extends Roo.util.Observable
33061  * Multi selection for a TreePanel.
33062  * @param {Object} cfg Configuration
33063  */
33064 Roo.tree.MultiSelectionModel = function(){
33065    this.selNodes = [];
33066    this.selMap = {};
33067    this.addEvents({
33068        /**
33069         * @event selectionchange
33070         * Fires when the selected nodes change
33071         * @param {MultiSelectionModel} this
33072         * @param {Array} nodes Array of the selected nodes
33073         */
33074        "selectionchange" : true
33075    });
33076    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33077    
33078 };
33079
33080 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33081     init : function(tree){
33082         this.tree = tree;
33083         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33084         tree.on("click", this.onNodeClick, this);
33085     },
33086     
33087     onNodeClick : function(node, e){
33088         this.select(node, e, e.ctrlKey);
33089     },
33090     
33091     /**
33092      * Select a node.
33093      * @param {TreeNode} node The node to select
33094      * @param {EventObject} e (optional) An event associated with the selection
33095      * @param {Boolean} keepExisting True to retain existing selections
33096      * @return {TreeNode} The selected node
33097      */
33098     select : function(node, e, keepExisting){
33099         if(keepExisting !== true){
33100             this.clearSelections(true);
33101         }
33102         if(this.isSelected(node)){
33103             this.lastSelNode = node;
33104             return node;
33105         }
33106         this.selNodes.push(node);
33107         this.selMap[node.id] = node;
33108         this.lastSelNode = node;
33109         node.ui.onSelectedChange(true);
33110         this.fireEvent("selectionchange", this, this.selNodes);
33111         return node;
33112     },
33113     
33114     /**
33115      * Deselect a node.
33116      * @param {TreeNode} node The node to unselect
33117      */
33118     unselect : function(node){
33119         if(this.selMap[node.id]){
33120             node.ui.onSelectedChange(false);
33121             var sn = this.selNodes;
33122             var index = -1;
33123             if(sn.indexOf){
33124                 index = sn.indexOf(node);
33125             }else{
33126                 for(var i = 0, len = sn.length; i < len; i++){
33127                     if(sn[i] == node){
33128                         index = i;
33129                         break;
33130                     }
33131                 }
33132             }
33133             if(index != -1){
33134                 this.selNodes.splice(index, 1);
33135             }
33136             delete this.selMap[node.id];
33137             this.fireEvent("selectionchange", this, this.selNodes);
33138         }
33139     },
33140     
33141     /**
33142      * Clear all selections
33143      */
33144     clearSelections : function(suppressEvent){
33145         var sn = this.selNodes;
33146         if(sn.length > 0){
33147             for(var i = 0, len = sn.length; i < len; i++){
33148                 sn[i].ui.onSelectedChange(false);
33149             }
33150             this.selNodes = [];
33151             this.selMap = {};
33152             if(suppressEvent !== true){
33153                 this.fireEvent("selectionchange", this, this.selNodes);
33154             }
33155         }
33156     },
33157     
33158     /**
33159      * Returns true if the node is selected
33160      * @param {TreeNode} node The node to check
33161      * @return {Boolean}
33162      */
33163     isSelected : function(node){
33164         return this.selMap[node.id] ? true : false;  
33165     },
33166     
33167     /**
33168      * Returns an array of the selected nodes
33169      * @return {Array}
33170      */
33171     getSelectedNodes : function(){
33172         return this.selNodes;    
33173     },
33174
33175     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33176
33177     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33178
33179     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33180 });/*
33181  * Based on:
33182  * Ext JS Library 1.1.1
33183  * Copyright(c) 2006-2007, Ext JS, LLC.
33184  *
33185  * Originally Released Under LGPL - original licence link has changed is not relivant.
33186  *
33187  * Fork - LGPL
33188  * <script type="text/javascript">
33189  */
33190  
33191 /**
33192  * @class Roo.tree.TreeNode
33193  * @extends Roo.data.Node
33194  * @cfg {String} text The text for this node
33195  * @cfg {Boolean} expanded true to start the node expanded
33196  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33197  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33198  * @cfg {Boolean} disabled true to start the node disabled
33199  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33200  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33201  * @cfg {String} cls A css class to be added to the node
33202  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33203  * @cfg {String} href URL of the link used for the node (defaults to #)
33204  * @cfg {String} hrefTarget target frame for the link
33205  * @cfg {String} qtip An Ext QuickTip for the node
33206  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33207  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33208  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33209  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33210  * (defaults to undefined with no checkbox rendered)
33211  * @constructor
33212  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33213  */
33214 Roo.tree.TreeNode = function(attributes){
33215     attributes = attributes || {};
33216     if(typeof attributes == "string"){
33217         attributes = {text: attributes};
33218     }
33219     this.childrenRendered = false;
33220     this.rendered = false;
33221     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33222     this.expanded = attributes.expanded === true;
33223     this.isTarget = attributes.isTarget !== false;
33224     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33225     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33226
33227     /**
33228      * Read-only. The text for this node. To change it use setText().
33229      * @type String
33230      */
33231     this.text = attributes.text;
33232     /**
33233      * True if this node is disabled.
33234      * @type Boolean
33235      */
33236     this.disabled = attributes.disabled === true;
33237
33238     this.addEvents({
33239         /**
33240         * @event textchange
33241         * Fires when the text for this node is changed
33242         * @param {Node} this This node
33243         * @param {String} text The new text
33244         * @param {String} oldText The old text
33245         */
33246         "textchange" : true,
33247         /**
33248         * @event beforeexpand
33249         * Fires before this node is expanded, return false to cancel.
33250         * @param {Node} this This node
33251         * @param {Boolean} deep
33252         * @param {Boolean} anim
33253         */
33254         "beforeexpand" : true,
33255         /**
33256         * @event beforecollapse
33257         * Fires before this node is collapsed, return false to cancel.
33258         * @param {Node} this This node
33259         * @param {Boolean} deep
33260         * @param {Boolean} anim
33261         */
33262         "beforecollapse" : true,
33263         /**
33264         * @event expand
33265         * Fires when this node is expanded
33266         * @param {Node} this This node
33267         */
33268         "expand" : true,
33269         /**
33270         * @event disabledchange
33271         * Fires when the disabled status of this node changes
33272         * @param {Node} this This node
33273         * @param {Boolean} disabled
33274         */
33275         "disabledchange" : true,
33276         /**
33277         * @event collapse
33278         * Fires when this node is collapsed
33279         * @param {Node} this This node
33280         */
33281         "collapse" : true,
33282         /**
33283         * @event beforeclick
33284         * Fires before click processing. Return false to cancel the default action.
33285         * @param {Node} this This node
33286         * @param {Roo.EventObject} e The event object
33287         */
33288         "beforeclick":true,
33289         /**
33290         * @event checkchange
33291         * Fires when a node with a checkbox's checked property changes
33292         * @param {Node} this This node
33293         * @param {Boolean} checked
33294         */
33295         "checkchange":true,
33296         /**
33297         * @event click
33298         * Fires when this node is clicked
33299         * @param {Node} this This node
33300         * @param {Roo.EventObject} e The event object
33301         */
33302         "click":true,
33303         /**
33304         * @event dblclick
33305         * Fires when this node is double clicked
33306         * @param {Node} this This node
33307         * @param {Roo.EventObject} e The event object
33308         */
33309         "dblclick":true,
33310         /**
33311         * @event contextmenu
33312         * Fires when this node is right clicked
33313         * @param {Node} this This node
33314         * @param {Roo.EventObject} e The event object
33315         */
33316         "contextmenu":true,
33317         /**
33318         * @event beforechildrenrendered
33319         * Fires right before the child nodes for this node are rendered
33320         * @param {Node} this This node
33321         */
33322         "beforechildrenrendered":true
33323     });
33324
33325     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33326
33327     /**
33328      * Read-only. The UI for this node
33329      * @type TreeNodeUI
33330      */
33331     this.ui = new uiClass(this);
33332     
33333     // finally support items[]
33334     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33335         return;
33336     }
33337     
33338     
33339     Roo.each(this.attributes.items, function(c) {
33340         this.appendChild(Roo.factory(c,Roo.Tree));
33341     }, this);
33342     delete this.attributes.items;
33343     
33344     
33345     
33346 };
33347 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33348     preventHScroll: true,
33349     /**
33350      * Returns true if this node is expanded
33351      * @return {Boolean}
33352      */
33353     isExpanded : function(){
33354         return this.expanded;
33355     },
33356
33357     /**
33358      * Returns the UI object for this node
33359      * @return {TreeNodeUI}
33360      */
33361     getUI : function(){
33362         return this.ui;
33363     },
33364
33365     // private override
33366     setFirstChild : function(node){
33367         var of = this.firstChild;
33368         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33369         if(this.childrenRendered && of && node != of){
33370             of.renderIndent(true, true);
33371         }
33372         if(this.rendered){
33373             this.renderIndent(true, true);
33374         }
33375     },
33376
33377     // private override
33378     setLastChild : function(node){
33379         var ol = this.lastChild;
33380         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33381         if(this.childrenRendered && ol && node != ol){
33382             ol.renderIndent(true, true);
33383         }
33384         if(this.rendered){
33385             this.renderIndent(true, true);
33386         }
33387     },
33388
33389     // these methods are overridden to provide lazy rendering support
33390     // private override
33391     appendChild : function()
33392     {
33393         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33394         if(node && this.childrenRendered){
33395             node.render();
33396         }
33397         this.ui.updateExpandIcon();
33398         return node;
33399     },
33400
33401     // private override
33402     removeChild : function(node){
33403         this.ownerTree.getSelectionModel().unselect(node);
33404         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33405         // if it's been rendered remove dom node
33406         if(this.childrenRendered){
33407             node.ui.remove();
33408         }
33409         if(this.childNodes.length < 1){
33410             this.collapse(false, false);
33411         }else{
33412             this.ui.updateExpandIcon();
33413         }
33414         if(!this.firstChild) {
33415             this.childrenRendered = false;
33416         }
33417         return node;
33418     },
33419
33420     // private override
33421     insertBefore : function(node, refNode){
33422         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33423         if(newNode && refNode && this.childrenRendered){
33424             node.render();
33425         }
33426         this.ui.updateExpandIcon();
33427         return newNode;
33428     },
33429
33430     /**
33431      * Sets the text for this node
33432      * @param {String} text
33433      */
33434     setText : function(text){
33435         var oldText = this.text;
33436         this.text = text;
33437         this.attributes.text = text;
33438         if(this.rendered){ // event without subscribing
33439             this.ui.onTextChange(this, text, oldText);
33440         }
33441         this.fireEvent("textchange", this, text, oldText);
33442     },
33443
33444     /**
33445      * Triggers selection of this node
33446      */
33447     select : function(){
33448         this.getOwnerTree().getSelectionModel().select(this);
33449     },
33450
33451     /**
33452      * Triggers deselection of this node
33453      */
33454     unselect : function(){
33455         this.getOwnerTree().getSelectionModel().unselect(this);
33456     },
33457
33458     /**
33459      * Returns true if this node is selected
33460      * @return {Boolean}
33461      */
33462     isSelected : function(){
33463         return this.getOwnerTree().getSelectionModel().isSelected(this);
33464     },
33465
33466     /**
33467      * Expand this node.
33468      * @param {Boolean} deep (optional) True to expand all children as well
33469      * @param {Boolean} anim (optional) false to cancel the default animation
33470      * @param {Function} callback (optional) A callback to be called when
33471      * expanding this node completes (does not wait for deep expand to complete).
33472      * Called with 1 parameter, this node.
33473      */
33474     expand : function(deep, anim, callback){
33475         if(!this.expanded){
33476             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33477                 return;
33478             }
33479             if(!this.childrenRendered){
33480                 this.renderChildren();
33481             }
33482             this.expanded = true;
33483             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33484                 this.ui.animExpand(function(){
33485                     this.fireEvent("expand", this);
33486                     if(typeof callback == "function"){
33487                         callback(this);
33488                     }
33489                     if(deep === true){
33490                         this.expandChildNodes(true);
33491                     }
33492                 }.createDelegate(this));
33493                 return;
33494             }else{
33495                 this.ui.expand();
33496                 this.fireEvent("expand", this);
33497                 if(typeof callback == "function"){
33498                     callback(this);
33499                 }
33500             }
33501         }else{
33502            if(typeof callback == "function"){
33503                callback(this);
33504            }
33505         }
33506         if(deep === true){
33507             this.expandChildNodes(true);
33508         }
33509     },
33510
33511     isHiddenRoot : function(){
33512         return this.isRoot && !this.getOwnerTree().rootVisible;
33513     },
33514
33515     /**
33516      * Collapse this node.
33517      * @param {Boolean} deep (optional) True to collapse all children as well
33518      * @param {Boolean} anim (optional) false to cancel the default animation
33519      */
33520     collapse : function(deep, anim){
33521         if(this.expanded && !this.isHiddenRoot()){
33522             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33523                 return;
33524             }
33525             this.expanded = false;
33526             if((this.getOwnerTree().animate && anim !== false) || anim){
33527                 this.ui.animCollapse(function(){
33528                     this.fireEvent("collapse", this);
33529                     if(deep === true){
33530                         this.collapseChildNodes(true);
33531                     }
33532                 }.createDelegate(this));
33533                 return;
33534             }else{
33535                 this.ui.collapse();
33536                 this.fireEvent("collapse", this);
33537             }
33538         }
33539         if(deep === true){
33540             var cs = this.childNodes;
33541             for(var i = 0, len = cs.length; i < len; i++) {
33542                 cs[i].collapse(true, false);
33543             }
33544         }
33545     },
33546
33547     // private
33548     delayedExpand : function(delay){
33549         if(!this.expandProcId){
33550             this.expandProcId = this.expand.defer(delay, this);
33551         }
33552     },
33553
33554     // private
33555     cancelExpand : function(){
33556         if(this.expandProcId){
33557             clearTimeout(this.expandProcId);
33558         }
33559         this.expandProcId = false;
33560     },
33561
33562     /**
33563      * Toggles expanded/collapsed state of the node
33564      */
33565     toggle : function(){
33566         if(this.expanded){
33567             this.collapse();
33568         }else{
33569             this.expand();
33570         }
33571     },
33572
33573     /**
33574      * Ensures all parent nodes are expanded
33575      */
33576     ensureVisible : function(callback){
33577         var tree = this.getOwnerTree();
33578         tree.expandPath(this.parentNode.getPath(), false, function(){
33579             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33580             Roo.callback(callback);
33581         }.createDelegate(this));
33582     },
33583
33584     /**
33585      * Expand all child nodes
33586      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33587      */
33588     expandChildNodes : function(deep){
33589         var cs = this.childNodes;
33590         for(var i = 0, len = cs.length; i < len; i++) {
33591                 cs[i].expand(deep);
33592         }
33593     },
33594
33595     /**
33596      * Collapse all child nodes
33597      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33598      */
33599     collapseChildNodes : function(deep){
33600         var cs = this.childNodes;
33601         for(var i = 0, len = cs.length; i < len; i++) {
33602                 cs[i].collapse(deep);
33603         }
33604     },
33605
33606     /**
33607      * Disables this node
33608      */
33609     disable : function(){
33610         this.disabled = true;
33611         this.unselect();
33612         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33613             this.ui.onDisableChange(this, true);
33614         }
33615         this.fireEvent("disabledchange", this, true);
33616     },
33617
33618     /**
33619      * Enables this node
33620      */
33621     enable : function(){
33622         this.disabled = false;
33623         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33624             this.ui.onDisableChange(this, false);
33625         }
33626         this.fireEvent("disabledchange", this, false);
33627     },
33628
33629     // private
33630     renderChildren : function(suppressEvent){
33631         if(suppressEvent !== false){
33632             this.fireEvent("beforechildrenrendered", this);
33633         }
33634         var cs = this.childNodes;
33635         for(var i = 0, len = cs.length; i < len; i++){
33636             cs[i].render(true);
33637         }
33638         this.childrenRendered = true;
33639     },
33640
33641     // private
33642     sort : function(fn, scope){
33643         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33644         if(this.childrenRendered){
33645             var cs = this.childNodes;
33646             for(var i = 0, len = cs.length; i < len; i++){
33647                 cs[i].render(true);
33648             }
33649         }
33650     },
33651
33652     // private
33653     render : function(bulkRender){
33654         this.ui.render(bulkRender);
33655         if(!this.rendered){
33656             this.rendered = true;
33657             if(this.expanded){
33658                 this.expanded = false;
33659                 this.expand(false, false);
33660             }
33661         }
33662     },
33663
33664     // private
33665     renderIndent : function(deep, refresh){
33666         if(refresh){
33667             this.ui.childIndent = null;
33668         }
33669         this.ui.renderIndent();
33670         if(deep === true && this.childrenRendered){
33671             var cs = this.childNodes;
33672             for(var i = 0, len = cs.length; i < len; i++){
33673                 cs[i].renderIndent(true, refresh);
33674             }
33675         }
33676     }
33677 });/*
33678  * Based on:
33679  * Ext JS Library 1.1.1
33680  * Copyright(c) 2006-2007, Ext JS, LLC.
33681  *
33682  * Originally Released Under LGPL - original licence link has changed is not relivant.
33683  *
33684  * Fork - LGPL
33685  * <script type="text/javascript">
33686  */
33687  
33688 /**
33689  * @class Roo.tree.AsyncTreeNode
33690  * @extends Roo.tree.TreeNode
33691  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33692  * @constructor
33693  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33694  */
33695  Roo.tree.AsyncTreeNode = function(config){
33696     this.loaded = false;
33697     this.loading = false;
33698     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33699     /**
33700     * @event beforeload
33701     * Fires before this node is loaded, return false to cancel
33702     * @param {Node} this This node
33703     */
33704     this.addEvents({'beforeload':true, 'load': true});
33705     /**
33706     * @event load
33707     * Fires when this node is loaded
33708     * @param {Node} this This node
33709     */
33710     /**
33711      * The loader used by this node (defaults to using the tree's defined loader)
33712      * @type TreeLoader
33713      * @property loader
33714      */
33715 };
33716 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33717     expand : function(deep, anim, callback){
33718         if(this.loading){ // if an async load is already running, waiting til it's done
33719             var timer;
33720             var f = function(){
33721                 if(!this.loading){ // done loading
33722                     clearInterval(timer);
33723                     this.expand(deep, anim, callback);
33724                 }
33725             }.createDelegate(this);
33726             timer = setInterval(f, 200);
33727             return;
33728         }
33729         if(!this.loaded){
33730             if(this.fireEvent("beforeload", this) === false){
33731                 return;
33732             }
33733             this.loading = true;
33734             this.ui.beforeLoad(this);
33735             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33736             if(loader){
33737                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33738                 return;
33739             }
33740         }
33741         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33742     },
33743     
33744     /**
33745      * Returns true if this node is currently loading
33746      * @return {Boolean}
33747      */
33748     isLoading : function(){
33749         return this.loading;  
33750     },
33751     
33752     loadComplete : function(deep, anim, callback){
33753         this.loading = false;
33754         this.loaded = true;
33755         this.ui.afterLoad(this);
33756         this.fireEvent("load", this);
33757         this.expand(deep, anim, callback);
33758     },
33759     
33760     /**
33761      * Returns true if this node has been loaded
33762      * @return {Boolean}
33763      */
33764     isLoaded : function(){
33765         return this.loaded;
33766     },
33767     
33768     hasChildNodes : function(){
33769         if(!this.isLeaf() && !this.loaded){
33770             return true;
33771         }else{
33772             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33773         }
33774     },
33775
33776     /**
33777      * Trigger a reload for this node
33778      * @param {Function} callback
33779      */
33780     reload : function(callback){
33781         this.collapse(false, false);
33782         while(this.firstChild){
33783             this.removeChild(this.firstChild);
33784         }
33785         this.childrenRendered = false;
33786         this.loaded = false;
33787         if(this.isHiddenRoot()){
33788             this.expanded = false;
33789         }
33790         this.expand(false, false, callback);
33791     }
33792 });/*
33793  * Based on:
33794  * Ext JS Library 1.1.1
33795  * Copyright(c) 2006-2007, Ext JS, LLC.
33796  *
33797  * Originally Released Under LGPL - original licence link has changed is not relivant.
33798  *
33799  * Fork - LGPL
33800  * <script type="text/javascript">
33801  */
33802  
33803 /**
33804  * @class Roo.tree.TreeNodeUI
33805  * @constructor
33806  * @param {Object} node The node to render
33807  * The TreeNode UI implementation is separate from the
33808  * tree implementation. Unless you are customizing the tree UI,
33809  * you should never have to use this directly.
33810  */
33811 Roo.tree.TreeNodeUI = function(node){
33812     this.node = node;
33813     this.rendered = false;
33814     this.animating = false;
33815     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33816 };
33817
33818 Roo.tree.TreeNodeUI.prototype = {
33819     removeChild : function(node){
33820         if(this.rendered){
33821             this.ctNode.removeChild(node.ui.getEl());
33822         }
33823     },
33824
33825     beforeLoad : function(){
33826          this.addClass("x-tree-node-loading");
33827     },
33828
33829     afterLoad : function(){
33830          this.removeClass("x-tree-node-loading");
33831     },
33832
33833     onTextChange : function(node, text, oldText){
33834         if(this.rendered){
33835             this.textNode.innerHTML = text;
33836         }
33837     },
33838
33839     onDisableChange : function(node, state){
33840         this.disabled = state;
33841         if(state){
33842             this.addClass("x-tree-node-disabled");
33843         }else{
33844             this.removeClass("x-tree-node-disabled");
33845         }
33846     },
33847
33848     onSelectedChange : function(state){
33849         if(state){
33850             this.focus();
33851             this.addClass("x-tree-selected");
33852         }else{
33853             //this.blur();
33854             this.removeClass("x-tree-selected");
33855         }
33856     },
33857
33858     onMove : function(tree, node, oldParent, newParent, index, refNode){
33859         this.childIndent = null;
33860         if(this.rendered){
33861             var targetNode = newParent.ui.getContainer();
33862             if(!targetNode){//target not rendered
33863                 this.holder = document.createElement("div");
33864                 this.holder.appendChild(this.wrap);
33865                 return;
33866             }
33867             var insertBefore = refNode ? refNode.ui.getEl() : null;
33868             if(insertBefore){
33869                 targetNode.insertBefore(this.wrap, insertBefore);
33870             }else{
33871                 targetNode.appendChild(this.wrap);
33872             }
33873             this.node.renderIndent(true);
33874         }
33875     },
33876
33877     addClass : function(cls){
33878         if(this.elNode){
33879             Roo.fly(this.elNode).addClass(cls);
33880         }
33881     },
33882
33883     removeClass : function(cls){
33884         if(this.elNode){
33885             Roo.fly(this.elNode).removeClass(cls);
33886         }
33887     },
33888
33889     remove : function(){
33890         if(this.rendered){
33891             this.holder = document.createElement("div");
33892             this.holder.appendChild(this.wrap);
33893         }
33894     },
33895
33896     fireEvent : function(){
33897         return this.node.fireEvent.apply(this.node, arguments);
33898     },
33899
33900     initEvents : function(){
33901         this.node.on("move", this.onMove, this);
33902         var E = Roo.EventManager;
33903         var a = this.anchor;
33904
33905         var el = Roo.fly(a, '_treeui');
33906
33907         if(Roo.isOpera){ // opera render bug ignores the CSS
33908             el.setStyle("text-decoration", "none");
33909         }
33910
33911         el.on("click", this.onClick, this);
33912         el.on("dblclick", this.onDblClick, this);
33913
33914         if(this.checkbox){
33915             Roo.EventManager.on(this.checkbox,
33916                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33917         }
33918
33919         el.on("contextmenu", this.onContextMenu, this);
33920
33921         var icon = Roo.fly(this.iconNode);
33922         icon.on("click", this.onClick, this);
33923         icon.on("dblclick", this.onDblClick, this);
33924         icon.on("contextmenu", this.onContextMenu, this);
33925         E.on(this.ecNode, "click", this.ecClick, this, true);
33926
33927         if(this.node.disabled){
33928             this.addClass("x-tree-node-disabled");
33929         }
33930         if(this.node.hidden){
33931             this.addClass("x-tree-node-disabled");
33932         }
33933         var ot = this.node.getOwnerTree();
33934         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33935         if(dd && (!this.node.isRoot || ot.rootVisible)){
33936             Roo.dd.Registry.register(this.elNode, {
33937                 node: this.node,
33938                 handles: this.getDDHandles(),
33939                 isHandle: false
33940             });
33941         }
33942     },
33943
33944     getDDHandles : function(){
33945         return [this.iconNode, this.textNode];
33946     },
33947
33948     hide : function(){
33949         if(this.rendered){
33950             this.wrap.style.display = "none";
33951         }
33952     },
33953
33954     show : function(){
33955         if(this.rendered){
33956             this.wrap.style.display = "";
33957         }
33958     },
33959
33960     onContextMenu : function(e){
33961         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33962             e.preventDefault();
33963             this.focus();
33964             this.fireEvent("contextmenu", this.node, e);
33965         }
33966     },
33967
33968     onClick : function(e){
33969         if(this.dropping){
33970             e.stopEvent();
33971             return;
33972         }
33973         if(this.fireEvent("beforeclick", this.node, e) !== false){
33974             if(!this.disabled && this.node.attributes.href){
33975                 this.fireEvent("click", this.node, e);
33976                 return;
33977             }
33978             e.preventDefault();
33979             if(this.disabled){
33980                 return;
33981             }
33982
33983             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33984                 this.node.toggle();
33985             }
33986
33987             this.fireEvent("click", this.node, e);
33988         }else{
33989             e.stopEvent();
33990         }
33991     },
33992
33993     onDblClick : function(e){
33994         e.preventDefault();
33995         if(this.disabled){
33996             return;
33997         }
33998         if(this.checkbox){
33999             this.toggleCheck();
34000         }
34001         if(!this.animating && this.node.hasChildNodes()){
34002             this.node.toggle();
34003         }
34004         this.fireEvent("dblclick", this.node, e);
34005     },
34006
34007     onCheckChange : function(){
34008         var checked = this.checkbox.checked;
34009         this.node.attributes.checked = checked;
34010         this.fireEvent('checkchange', this.node, checked);
34011     },
34012
34013     ecClick : function(e){
34014         if(!this.animating && this.node.hasChildNodes()){
34015             this.node.toggle();
34016         }
34017     },
34018
34019     startDrop : function(){
34020         this.dropping = true;
34021     },
34022
34023     // delayed drop so the click event doesn't get fired on a drop
34024     endDrop : function(){
34025        setTimeout(function(){
34026            this.dropping = false;
34027        }.createDelegate(this), 50);
34028     },
34029
34030     expand : function(){
34031         this.updateExpandIcon();
34032         this.ctNode.style.display = "";
34033     },
34034
34035     focus : function(){
34036         if(!this.node.preventHScroll){
34037             try{this.anchor.focus();
34038             }catch(e){}
34039         }else if(!Roo.isIE){
34040             try{
34041                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34042                 var l = noscroll.scrollLeft;
34043                 this.anchor.focus();
34044                 noscroll.scrollLeft = l;
34045             }catch(e){}
34046         }
34047     },
34048
34049     toggleCheck : function(value){
34050         var cb = this.checkbox;
34051         if(cb){
34052             cb.checked = (value === undefined ? !cb.checked : value);
34053         }
34054     },
34055
34056     blur : function(){
34057         try{
34058             this.anchor.blur();
34059         }catch(e){}
34060     },
34061
34062     animExpand : function(callback){
34063         var ct = Roo.get(this.ctNode);
34064         ct.stopFx();
34065         if(!this.node.hasChildNodes()){
34066             this.updateExpandIcon();
34067             this.ctNode.style.display = "";
34068             Roo.callback(callback);
34069             return;
34070         }
34071         this.animating = true;
34072         this.updateExpandIcon();
34073
34074         ct.slideIn('t', {
34075            callback : function(){
34076                this.animating = false;
34077                Roo.callback(callback);
34078             },
34079             scope: this,
34080             duration: this.node.ownerTree.duration || .25
34081         });
34082     },
34083
34084     highlight : function(){
34085         var tree = this.node.getOwnerTree();
34086         Roo.fly(this.wrap).highlight(
34087             tree.hlColor || "C3DAF9",
34088             {endColor: tree.hlBaseColor}
34089         );
34090     },
34091
34092     collapse : function(){
34093         this.updateExpandIcon();
34094         this.ctNode.style.display = "none";
34095     },
34096
34097     animCollapse : function(callback){
34098         var ct = Roo.get(this.ctNode);
34099         ct.enableDisplayMode('block');
34100         ct.stopFx();
34101
34102         this.animating = true;
34103         this.updateExpandIcon();
34104
34105         ct.slideOut('t', {
34106             callback : function(){
34107                this.animating = false;
34108                Roo.callback(callback);
34109             },
34110             scope: this,
34111             duration: this.node.ownerTree.duration || .25
34112         });
34113     },
34114
34115     getContainer : function(){
34116         return this.ctNode;
34117     },
34118
34119     getEl : function(){
34120         return this.wrap;
34121     },
34122
34123     appendDDGhost : function(ghostNode){
34124         ghostNode.appendChild(this.elNode.cloneNode(true));
34125     },
34126
34127     getDDRepairXY : function(){
34128         return Roo.lib.Dom.getXY(this.iconNode);
34129     },
34130
34131     onRender : function(){
34132         this.render();
34133     },
34134
34135     render : function(bulkRender){
34136         var n = this.node, a = n.attributes;
34137         var targetNode = n.parentNode ?
34138               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34139
34140         if(!this.rendered){
34141             this.rendered = true;
34142
34143             this.renderElements(n, a, targetNode, bulkRender);
34144
34145             if(a.qtip){
34146                if(this.textNode.setAttributeNS){
34147                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34148                    if(a.qtipTitle){
34149                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34150                    }
34151                }else{
34152                    this.textNode.setAttribute("ext:qtip", a.qtip);
34153                    if(a.qtipTitle){
34154                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34155                    }
34156                }
34157             }else if(a.qtipCfg){
34158                 a.qtipCfg.target = Roo.id(this.textNode);
34159                 Roo.QuickTips.register(a.qtipCfg);
34160             }
34161             this.initEvents();
34162             if(!this.node.expanded){
34163                 this.updateExpandIcon();
34164             }
34165         }else{
34166             if(bulkRender === true) {
34167                 targetNode.appendChild(this.wrap);
34168             }
34169         }
34170     },
34171
34172     renderElements : function(n, a, targetNode, bulkRender)
34173     {
34174         // add some indent caching, this helps performance when rendering a large tree
34175         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34176         var t = n.getOwnerTree();
34177         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34178         if (typeof(n.attributes.html) != 'undefined') {
34179             txt = n.attributes.html;
34180         }
34181         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34182         var cb = typeof a.checked == 'boolean';
34183         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34184         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34185             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34186             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34187             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34188             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34189             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34190              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34191                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34192             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34193             "</li>"];
34194
34195         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34196             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34197                                 n.nextSibling.ui.getEl(), buf.join(""));
34198         }else{
34199             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34200         }
34201
34202         this.elNode = this.wrap.childNodes[0];
34203         this.ctNode = this.wrap.childNodes[1];
34204         var cs = this.elNode.childNodes;
34205         this.indentNode = cs[0];
34206         this.ecNode = cs[1];
34207         this.iconNode = cs[2];
34208         var index = 3;
34209         if(cb){
34210             this.checkbox = cs[3];
34211             index++;
34212         }
34213         this.anchor = cs[index];
34214         this.textNode = cs[index].firstChild;
34215     },
34216
34217     getAnchor : function(){
34218         return this.anchor;
34219     },
34220
34221     getTextEl : function(){
34222         return this.textNode;
34223     },
34224
34225     getIconEl : function(){
34226         return this.iconNode;
34227     },
34228
34229     isChecked : function(){
34230         return this.checkbox ? this.checkbox.checked : false;
34231     },
34232
34233     updateExpandIcon : function(){
34234         if(this.rendered){
34235             var n = this.node, c1, c2;
34236             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34237             var hasChild = n.hasChildNodes();
34238             if(hasChild){
34239                 if(n.expanded){
34240                     cls += "-minus";
34241                     c1 = "x-tree-node-collapsed";
34242                     c2 = "x-tree-node-expanded";
34243                 }else{
34244                     cls += "-plus";
34245                     c1 = "x-tree-node-expanded";
34246                     c2 = "x-tree-node-collapsed";
34247                 }
34248                 if(this.wasLeaf){
34249                     this.removeClass("x-tree-node-leaf");
34250                     this.wasLeaf = false;
34251                 }
34252                 if(this.c1 != c1 || this.c2 != c2){
34253                     Roo.fly(this.elNode).replaceClass(c1, c2);
34254                     this.c1 = c1; this.c2 = c2;
34255                 }
34256             }else{
34257                 // this changes non-leafs into leafs if they have no children.
34258                 // it's not very rational behaviour..
34259                 
34260                 if(!this.wasLeaf && this.node.leaf){
34261                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34262                     delete this.c1;
34263                     delete this.c2;
34264                     this.wasLeaf = true;
34265                 }
34266             }
34267             var ecc = "x-tree-ec-icon "+cls;
34268             if(this.ecc != ecc){
34269                 this.ecNode.className = ecc;
34270                 this.ecc = ecc;
34271             }
34272         }
34273     },
34274
34275     getChildIndent : function(){
34276         if(!this.childIndent){
34277             var buf = [];
34278             var p = this.node;
34279             while(p){
34280                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34281                     if(!p.isLast()) {
34282                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34283                     } else {
34284                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34285                     }
34286                 }
34287                 p = p.parentNode;
34288             }
34289             this.childIndent = buf.join("");
34290         }
34291         return this.childIndent;
34292     },
34293
34294     renderIndent : function(){
34295         if(this.rendered){
34296             var indent = "";
34297             var p = this.node.parentNode;
34298             if(p){
34299                 indent = p.ui.getChildIndent();
34300             }
34301             if(this.indentMarkup != indent){ // don't rerender if not required
34302                 this.indentNode.innerHTML = indent;
34303                 this.indentMarkup = indent;
34304             }
34305             this.updateExpandIcon();
34306         }
34307     }
34308 };
34309
34310 Roo.tree.RootTreeNodeUI = function(){
34311     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34312 };
34313 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34314     render : function(){
34315         if(!this.rendered){
34316             var targetNode = this.node.ownerTree.innerCt.dom;
34317             this.node.expanded = true;
34318             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34319             this.wrap = this.ctNode = targetNode.firstChild;
34320         }
34321     },
34322     collapse : function(){
34323     },
34324     expand : function(){
34325     }
34326 });/*
34327  * Based on:
34328  * Ext JS Library 1.1.1
34329  * Copyright(c) 2006-2007, Ext JS, LLC.
34330  *
34331  * Originally Released Under LGPL - original licence link has changed is not relivant.
34332  *
34333  * Fork - LGPL
34334  * <script type="text/javascript">
34335  */
34336 /**
34337  * @class Roo.tree.TreeLoader
34338  * @extends Roo.util.Observable
34339  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34340  * nodes from a specified URL. The response must be a javascript Array definition
34341  * who's elements are node definition objects. eg:
34342  * <pre><code>
34343 {  success : true,
34344    data :      [
34345    
34346     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34347     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34348     ]
34349 }
34350
34351
34352 </code></pre>
34353  * <br><br>
34354  * The old style respose with just an array is still supported, but not recommended.
34355  * <br><br>
34356  *
34357  * A server request is sent, and child nodes are loaded only when a node is expanded.
34358  * The loading node's id is passed to the server under the parameter name "node" to
34359  * enable the server to produce the correct child nodes.
34360  * <br><br>
34361  * To pass extra parameters, an event handler may be attached to the "beforeload"
34362  * event, and the parameters specified in the TreeLoader's baseParams property:
34363  * <pre><code>
34364     myTreeLoader.on("beforeload", function(treeLoader, node) {
34365         this.baseParams.category = node.attributes.category;
34366     }, this);
34367 </code></pre><
34368  * This would pass an HTTP parameter called "category" to the server containing
34369  * the value of the Node's "category" attribute.
34370  * @constructor
34371  * Creates a new Treeloader.
34372  * @param {Object} config A config object containing config properties.
34373  */
34374 Roo.tree.TreeLoader = function(config){
34375     this.baseParams = {};
34376     this.requestMethod = "POST";
34377     Roo.apply(this, config);
34378
34379     this.addEvents({
34380     
34381         /**
34382          * @event beforeload
34383          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34384          * @param {Object} This TreeLoader object.
34385          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34386          * @param {Object} callback The callback function specified in the {@link #load} call.
34387          */
34388         beforeload : true,
34389         /**
34390          * @event load
34391          * Fires when the node has been successfuly loaded.
34392          * @param {Object} This TreeLoader object.
34393          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34394          * @param {Object} response The response object containing the data from the server.
34395          */
34396         load : true,
34397         /**
34398          * @event loadexception
34399          * Fires if the network request failed.
34400          * @param {Object} This TreeLoader object.
34401          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34402          * @param {Object} response The response object containing the data from the server.
34403          */
34404         loadexception : true,
34405         /**
34406          * @event create
34407          * Fires before a node is created, enabling you to return custom Node types 
34408          * @param {Object} This TreeLoader object.
34409          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34410          */
34411         create : true
34412     });
34413
34414     Roo.tree.TreeLoader.superclass.constructor.call(this);
34415 };
34416
34417 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34418     /**
34419     * @cfg {String} dataUrl The URL from which to request a Json string which
34420     * specifies an array of node definition object representing the child nodes
34421     * to be loaded.
34422     */
34423     /**
34424     * @cfg {String} requestMethod either GET or POST
34425     * defaults to POST (due to BC)
34426     * to be loaded.
34427     */
34428     /**
34429     * @cfg {Object} baseParams (optional) An object containing properties which
34430     * specify HTTP parameters to be passed to each request for child nodes.
34431     */
34432     /**
34433     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34434     * created by this loader. If the attributes sent by the server have an attribute in this object,
34435     * they take priority.
34436     */
34437     /**
34438     * @cfg {Object} uiProviders (optional) An object containing properties which
34439     * 
34440     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34441     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34442     * <i>uiProvider</i> attribute of a returned child node is a string rather
34443     * than a reference to a TreeNodeUI implementation, this that string value
34444     * is used as a property name in the uiProviders object. You can define the provider named
34445     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34446     */
34447     uiProviders : {},
34448
34449     /**
34450     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34451     * child nodes before loading.
34452     */
34453     clearOnLoad : true,
34454
34455     /**
34456     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34457     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34458     * Grid query { data : [ .....] }
34459     */
34460     
34461     root : false,
34462      /**
34463     * @cfg {String} queryParam (optional) 
34464     * Name of the query as it will be passed on the querystring (defaults to 'node')
34465     * eg. the request will be ?node=[id]
34466     */
34467     
34468     
34469     queryParam: false,
34470     
34471     /**
34472      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34473      * This is called automatically when a node is expanded, but may be used to reload
34474      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34475      * @param {Roo.tree.TreeNode} node
34476      * @param {Function} callback
34477      */
34478     load : function(node, callback){
34479         if(this.clearOnLoad){
34480             while(node.firstChild){
34481                 node.removeChild(node.firstChild);
34482             }
34483         }
34484         if(node.attributes.children){ // preloaded json children
34485             var cs = node.attributes.children;
34486             for(var i = 0, len = cs.length; i < len; i++){
34487                 node.appendChild(this.createNode(cs[i]));
34488             }
34489             if(typeof callback == "function"){
34490                 callback();
34491             }
34492         }else if(this.dataUrl){
34493             this.requestData(node, callback);
34494         }
34495     },
34496
34497     getParams: function(node){
34498         var buf = [], bp = this.baseParams;
34499         for(var key in bp){
34500             if(typeof bp[key] != "function"){
34501                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34502             }
34503         }
34504         var n = this.queryParam === false ? 'node' : this.queryParam;
34505         buf.push(n + "=", encodeURIComponent(node.id));
34506         return buf.join("");
34507     },
34508
34509     requestData : function(node, callback){
34510         if(this.fireEvent("beforeload", this, node, callback) !== false){
34511             this.transId = Roo.Ajax.request({
34512                 method:this.requestMethod,
34513                 url: this.dataUrl||this.url,
34514                 success: this.handleResponse,
34515                 failure: this.handleFailure,
34516                 scope: this,
34517                 argument: {callback: callback, node: node},
34518                 params: this.getParams(node)
34519             });
34520         }else{
34521             // if the load is cancelled, make sure we notify
34522             // the node that we are done
34523             if(typeof callback == "function"){
34524                 callback();
34525             }
34526         }
34527     },
34528
34529     isLoading : function(){
34530         return this.transId ? true : false;
34531     },
34532
34533     abort : function(){
34534         if(this.isLoading()){
34535             Roo.Ajax.abort(this.transId);
34536         }
34537     },
34538
34539     // private
34540     createNode : function(attr)
34541     {
34542         // apply baseAttrs, nice idea Corey!
34543         if(this.baseAttrs){
34544             Roo.applyIf(attr, this.baseAttrs);
34545         }
34546         if(this.applyLoader !== false){
34547             attr.loader = this;
34548         }
34549         // uiProvider = depreciated..
34550         
34551         if(typeof(attr.uiProvider) == 'string'){
34552            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34553                 /**  eval:var:attr */ eval(attr.uiProvider);
34554         }
34555         if(typeof(this.uiProviders['default']) != 'undefined') {
34556             attr.uiProvider = this.uiProviders['default'];
34557         }
34558         
34559         this.fireEvent('create', this, attr);
34560         
34561         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34562         return(attr.leaf ?
34563                         new Roo.tree.TreeNode(attr) :
34564                         new Roo.tree.AsyncTreeNode(attr));
34565     },
34566
34567     processResponse : function(response, node, callback)
34568     {
34569         var json = response.responseText;
34570         try {
34571             
34572             var o = Roo.decode(json);
34573             
34574             if (this.root === false && typeof(o.success) != undefined) {
34575                 this.root = 'data'; // the default behaviour for list like data..
34576                 }
34577                 
34578             if (this.root !== false &&  !o.success) {
34579                 // it's a failure condition.
34580                 var a = response.argument;
34581                 this.fireEvent("loadexception", this, a.node, response);
34582                 Roo.log("Load failed - should have a handler really");
34583                 return;
34584             }
34585             
34586             
34587             
34588             if (this.root !== false) {
34589                  o = o[this.root];
34590             }
34591             
34592             for(var i = 0, len = o.length; i < len; i++){
34593                 var n = this.createNode(o[i]);
34594                 if(n){
34595                     node.appendChild(n);
34596                 }
34597             }
34598             if(typeof callback == "function"){
34599                 callback(this, node);
34600             }
34601         }catch(e){
34602             this.handleFailure(response);
34603         }
34604     },
34605
34606     handleResponse : function(response){
34607         this.transId = false;
34608         var a = response.argument;
34609         this.processResponse(response, a.node, a.callback);
34610         this.fireEvent("load", this, a.node, response);
34611     },
34612
34613     handleFailure : function(response)
34614     {
34615         // should handle failure better..
34616         this.transId = false;
34617         var a = response.argument;
34618         this.fireEvent("loadexception", this, a.node, response);
34619         if(typeof a.callback == "function"){
34620             a.callback(this, a.node);
34621         }
34622     }
34623 });/*
34624  * Based on:
34625  * Ext JS Library 1.1.1
34626  * Copyright(c) 2006-2007, Ext JS, LLC.
34627  *
34628  * Originally Released Under LGPL - original licence link has changed is not relivant.
34629  *
34630  * Fork - LGPL
34631  * <script type="text/javascript">
34632  */
34633
34634 /**
34635 * @class Roo.tree.TreeFilter
34636 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34637 * @param {TreePanel} tree
34638 * @param {Object} config (optional)
34639  */
34640 Roo.tree.TreeFilter = function(tree, config){
34641     this.tree = tree;
34642     this.filtered = {};
34643     Roo.apply(this, config);
34644 };
34645
34646 Roo.tree.TreeFilter.prototype = {
34647     clearBlank:false,
34648     reverse:false,
34649     autoClear:false,
34650     remove:false,
34651
34652      /**
34653      * Filter the data by a specific attribute.
34654      * @param {String/RegExp} value Either string that the attribute value
34655      * should start with or a RegExp to test against the attribute
34656      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34657      * @param {TreeNode} startNode (optional) The node to start the filter at.
34658      */
34659     filter : function(value, attr, startNode){
34660         attr = attr || "text";
34661         var f;
34662         if(typeof value == "string"){
34663             var vlen = value.length;
34664             // auto clear empty filter
34665             if(vlen == 0 && this.clearBlank){
34666                 this.clear();
34667                 return;
34668             }
34669             value = value.toLowerCase();
34670             f = function(n){
34671                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34672             };
34673         }else if(value.exec){ // regex?
34674             f = function(n){
34675                 return value.test(n.attributes[attr]);
34676             };
34677         }else{
34678             throw 'Illegal filter type, must be string or regex';
34679         }
34680         this.filterBy(f, null, startNode);
34681         },
34682
34683     /**
34684      * Filter by a function. The passed function will be called with each
34685      * node in the tree (or from the startNode). If the function returns true, the node is kept
34686      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34687      * @param {Function} fn The filter function
34688      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34689      */
34690     filterBy : function(fn, scope, startNode){
34691         startNode = startNode || this.tree.root;
34692         if(this.autoClear){
34693             this.clear();
34694         }
34695         var af = this.filtered, rv = this.reverse;
34696         var f = function(n){
34697             if(n == startNode){
34698                 return true;
34699             }
34700             if(af[n.id]){
34701                 return false;
34702             }
34703             var m = fn.call(scope || n, n);
34704             if(!m || rv){
34705                 af[n.id] = n;
34706                 n.ui.hide();
34707                 return false;
34708             }
34709             return true;
34710         };
34711         startNode.cascade(f);
34712         if(this.remove){
34713            for(var id in af){
34714                if(typeof id != "function"){
34715                    var n = af[id];
34716                    if(n && n.parentNode){
34717                        n.parentNode.removeChild(n);
34718                    }
34719                }
34720            }
34721         }
34722     },
34723
34724     /**
34725      * Clears the current filter. Note: with the "remove" option
34726      * set a filter cannot be cleared.
34727      */
34728     clear : function(){
34729         var t = this.tree;
34730         var af = this.filtered;
34731         for(var id in af){
34732             if(typeof id != "function"){
34733                 var n = af[id];
34734                 if(n){
34735                     n.ui.show();
34736                 }
34737             }
34738         }
34739         this.filtered = {};
34740     }
34741 };
34742 /*
34743  * Based on:
34744  * Ext JS Library 1.1.1
34745  * Copyright(c) 2006-2007, Ext JS, LLC.
34746  *
34747  * Originally Released Under LGPL - original licence link has changed is not relivant.
34748  *
34749  * Fork - LGPL
34750  * <script type="text/javascript">
34751  */
34752  
34753
34754 /**
34755  * @class Roo.tree.TreeSorter
34756  * Provides sorting of nodes in a TreePanel
34757  * 
34758  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34759  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34760  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34761  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34762  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34763  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34764  * @constructor
34765  * @param {TreePanel} tree
34766  * @param {Object} config
34767  */
34768 Roo.tree.TreeSorter = function(tree, config){
34769     Roo.apply(this, config);
34770     tree.on("beforechildrenrendered", this.doSort, this);
34771     tree.on("append", this.updateSort, this);
34772     tree.on("insert", this.updateSort, this);
34773     
34774     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34775     var p = this.property || "text";
34776     var sortType = this.sortType;
34777     var fs = this.folderSort;
34778     var cs = this.caseSensitive === true;
34779     var leafAttr = this.leafAttr || 'leaf';
34780
34781     this.sortFn = function(n1, n2){
34782         if(fs){
34783             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34784                 return 1;
34785             }
34786             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34787                 return -1;
34788             }
34789         }
34790         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34791         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34792         if(v1 < v2){
34793                         return dsc ? +1 : -1;
34794                 }else if(v1 > v2){
34795                         return dsc ? -1 : +1;
34796         }else{
34797                 return 0;
34798         }
34799     };
34800 };
34801
34802 Roo.tree.TreeSorter.prototype = {
34803     doSort : function(node){
34804         node.sort(this.sortFn);
34805     },
34806     
34807     compareNodes : function(n1, n2){
34808         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34809     },
34810     
34811     updateSort : function(tree, node){
34812         if(node.childrenRendered){
34813             this.doSort.defer(1, this, [node]);
34814         }
34815     }
34816 };/*
34817  * Based on:
34818  * Ext JS Library 1.1.1
34819  * Copyright(c) 2006-2007, Ext JS, LLC.
34820  *
34821  * Originally Released Under LGPL - original licence link has changed is not relivant.
34822  *
34823  * Fork - LGPL
34824  * <script type="text/javascript">
34825  */
34826
34827 if(Roo.dd.DropZone){
34828     
34829 Roo.tree.TreeDropZone = function(tree, config){
34830     this.allowParentInsert = false;
34831     this.allowContainerDrop = false;
34832     this.appendOnly = false;
34833     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34834     this.tree = tree;
34835     this.lastInsertClass = "x-tree-no-status";
34836     this.dragOverData = {};
34837 };
34838
34839 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34840     ddGroup : "TreeDD",
34841     scroll:  true,
34842     
34843     expandDelay : 1000,
34844     
34845     expandNode : function(node){
34846         if(node.hasChildNodes() && !node.isExpanded()){
34847             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34848         }
34849     },
34850     
34851     queueExpand : function(node){
34852         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34853     },
34854     
34855     cancelExpand : function(){
34856         if(this.expandProcId){
34857             clearTimeout(this.expandProcId);
34858             this.expandProcId = false;
34859         }
34860     },
34861     
34862     isValidDropPoint : function(n, pt, dd, e, data){
34863         if(!n || !data){ return false; }
34864         var targetNode = n.node;
34865         var dropNode = data.node;
34866         // default drop rules
34867         if(!(targetNode && targetNode.isTarget && pt)){
34868             return false;
34869         }
34870         if(pt == "append" && targetNode.allowChildren === false){
34871             return false;
34872         }
34873         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34874             return false;
34875         }
34876         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34877             return false;
34878         }
34879         // reuse the object
34880         var overEvent = this.dragOverData;
34881         overEvent.tree = this.tree;
34882         overEvent.target = targetNode;
34883         overEvent.data = data;
34884         overEvent.point = pt;
34885         overEvent.source = dd;
34886         overEvent.rawEvent = e;
34887         overEvent.dropNode = dropNode;
34888         overEvent.cancel = false;  
34889         var result = this.tree.fireEvent("nodedragover", overEvent);
34890         return overEvent.cancel === false && result !== false;
34891     },
34892     
34893     getDropPoint : function(e, n, dd)
34894     {
34895         var tn = n.node;
34896         if(tn.isRoot){
34897             return tn.allowChildren !== false ? "append" : false; // always append for root
34898         }
34899         var dragEl = n.ddel;
34900         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34901         var y = Roo.lib.Event.getPageY(e);
34902         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34903         
34904         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34905         var noAppend = tn.allowChildren === false;
34906         if(this.appendOnly || tn.parentNode.allowChildren === false){
34907             return noAppend ? false : "append";
34908         }
34909         var noBelow = false;
34910         if(!this.allowParentInsert){
34911             noBelow = tn.hasChildNodes() && tn.isExpanded();
34912         }
34913         var q = (b - t) / (noAppend ? 2 : 3);
34914         if(y >= t && y < (t + q)){
34915             return "above";
34916         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34917             return "below";
34918         }else{
34919             return "append";
34920         }
34921     },
34922     
34923     onNodeEnter : function(n, dd, e, data)
34924     {
34925         this.cancelExpand();
34926     },
34927     
34928     onNodeOver : function(n, dd, e, data)
34929     {
34930        
34931         var pt = this.getDropPoint(e, n, dd);
34932         var node = n.node;
34933         
34934         // auto node expand check
34935         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34936             this.queueExpand(node);
34937         }else if(pt != "append"){
34938             this.cancelExpand();
34939         }
34940         
34941         // set the insert point style on the target node
34942         var returnCls = this.dropNotAllowed;
34943         if(this.isValidDropPoint(n, pt, dd, e, data)){
34944            if(pt){
34945                var el = n.ddel;
34946                var cls;
34947                if(pt == "above"){
34948                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34949                    cls = "x-tree-drag-insert-above";
34950                }else if(pt == "below"){
34951                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34952                    cls = "x-tree-drag-insert-below";
34953                }else{
34954                    returnCls = "x-tree-drop-ok-append";
34955                    cls = "x-tree-drag-append";
34956                }
34957                if(this.lastInsertClass != cls){
34958                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34959                    this.lastInsertClass = cls;
34960                }
34961            }
34962        }
34963        return returnCls;
34964     },
34965     
34966     onNodeOut : function(n, dd, e, data){
34967         
34968         this.cancelExpand();
34969         this.removeDropIndicators(n);
34970     },
34971     
34972     onNodeDrop : function(n, dd, e, data){
34973         var point = this.getDropPoint(e, n, dd);
34974         var targetNode = n.node;
34975         targetNode.ui.startDrop();
34976         if(!this.isValidDropPoint(n, point, dd, e, data)){
34977             targetNode.ui.endDrop();
34978             return false;
34979         }
34980         // first try to find the drop node
34981         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34982         var dropEvent = {
34983             tree : this.tree,
34984             target: targetNode,
34985             data: data,
34986             point: point,
34987             source: dd,
34988             rawEvent: e,
34989             dropNode: dropNode,
34990             cancel: !dropNode   
34991         };
34992         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34993         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34994             targetNode.ui.endDrop();
34995             return false;
34996         }
34997         // allow target changing
34998         targetNode = dropEvent.target;
34999         if(point == "append" && !targetNode.isExpanded()){
35000             targetNode.expand(false, null, function(){
35001                 this.completeDrop(dropEvent);
35002             }.createDelegate(this));
35003         }else{
35004             this.completeDrop(dropEvent);
35005         }
35006         return true;
35007     },
35008     
35009     completeDrop : function(de){
35010         var ns = de.dropNode, p = de.point, t = de.target;
35011         if(!(ns instanceof Array)){
35012             ns = [ns];
35013         }
35014         var n;
35015         for(var i = 0, len = ns.length; i < len; i++){
35016             n = ns[i];
35017             if(p == "above"){
35018                 t.parentNode.insertBefore(n, t);
35019             }else if(p == "below"){
35020                 t.parentNode.insertBefore(n, t.nextSibling);
35021             }else{
35022                 t.appendChild(n);
35023             }
35024         }
35025         n.ui.focus();
35026         if(this.tree.hlDrop){
35027             n.ui.highlight();
35028         }
35029         t.ui.endDrop();
35030         this.tree.fireEvent("nodedrop", de);
35031     },
35032     
35033     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35034         if(this.tree.hlDrop){
35035             dropNode.ui.focus();
35036             dropNode.ui.highlight();
35037         }
35038         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35039     },
35040     
35041     getTree : function(){
35042         return this.tree;
35043     },
35044     
35045     removeDropIndicators : function(n){
35046         if(n && n.ddel){
35047             var el = n.ddel;
35048             Roo.fly(el).removeClass([
35049                     "x-tree-drag-insert-above",
35050                     "x-tree-drag-insert-below",
35051                     "x-tree-drag-append"]);
35052             this.lastInsertClass = "_noclass";
35053         }
35054     },
35055     
35056     beforeDragDrop : function(target, e, id){
35057         this.cancelExpand();
35058         return true;
35059     },
35060     
35061     afterRepair : function(data){
35062         if(data && Roo.enableFx){
35063             data.node.ui.highlight();
35064         }
35065         this.hideProxy();
35066     } 
35067     
35068 });
35069
35070 }
35071 /*
35072  * Based on:
35073  * Ext JS Library 1.1.1
35074  * Copyright(c) 2006-2007, Ext JS, LLC.
35075  *
35076  * Originally Released Under LGPL - original licence link has changed is not relivant.
35077  *
35078  * Fork - LGPL
35079  * <script type="text/javascript">
35080  */
35081  
35082
35083 if(Roo.dd.DragZone){
35084 Roo.tree.TreeDragZone = function(tree, config){
35085     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35086     this.tree = tree;
35087 };
35088
35089 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35090     ddGroup : "TreeDD",
35091    
35092     onBeforeDrag : function(data, e){
35093         var n = data.node;
35094         return n && n.draggable && !n.disabled;
35095     },
35096      
35097     
35098     onInitDrag : function(e){
35099         var data = this.dragData;
35100         this.tree.getSelectionModel().select(data.node);
35101         this.proxy.update("");
35102         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35103         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35104     },
35105     
35106     getRepairXY : function(e, data){
35107         return data.node.ui.getDDRepairXY();
35108     },
35109     
35110     onEndDrag : function(data, e){
35111         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35112         
35113         
35114     },
35115     
35116     onValidDrop : function(dd, e, id){
35117         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35118         this.hideProxy();
35119     },
35120     
35121     beforeInvalidDrop : function(e, id){
35122         // this scrolls the original position back into view
35123         var sm = this.tree.getSelectionModel();
35124         sm.clearSelections();
35125         sm.select(this.dragData.node);
35126     }
35127 });
35128 }/*
35129  * Based on:
35130  * Ext JS Library 1.1.1
35131  * Copyright(c) 2006-2007, Ext JS, LLC.
35132  *
35133  * Originally Released Under LGPL - original licence link has changed is not relivant.
35134  *
35135  * Fork - LGPL
35136  * <script type="text/javascript">
35137  */
35138 /**
35139  * @class Roo.tree.TreeEditor
35140  * @extends Roo.Editor
35141  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35142  * as the editor field.
35143  * @constructor
35144  * @param {Object} config (used to be the tree panel.)
35145  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35146  * 
35147  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35148  * @cfg {Roo.form.TextField|Object} field The field configuration
35149  *
35150  * 
35151  */
35152 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35153     var tree = config;
35154     var field;
35155     if (oldconfig) { // old style..
35156         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35157     } else {
35158         // new style..
35159         tree = config.tree;
35160         config.field = config.field  || {};
35161         config.field.xtype = 'TextField';
35162         field = Roo.factory(config.field, Roo.form);
35163     }
35164     config = config || {};
35165     
35166     
35167     this.addEvents({
35168         /**
35169          * @event beforenodeedit
35170          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35171          * false from the handler of this event.
35172          * @param {Editor} this
35173          * @param {Roo.tree.Node} node 
35174          */
35175         "beforenodeedit" : true
35176     });
35177     
35178     //Roo.log(config);
35179     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35180
35181     this.tree = tree;
35182
35183     tree.on('beforeclick', this.beforeNodeClick, this);
35184     tree.getTreeEl().on('mousedown', this.hide, this);
35185     this.on('complete', this.updateNode, this);
35186     this.on('beforestartedit', this.fitToTree, this);
35187     this.on('startedit', this.bindScroll, this, {delay:10});
35188     this.on('specialkey', this.onSpecialKey, this);
35189 };
35190
35191 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35192     /**
35193      * @cfg {String} alignment
35194      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35195      */
35196     alignment: "l-l",
35197     // inherit
35198     autoSize: false,
35199     /**
35200      * @cfg {Boolean} hideEl
35201      * True to hide the bound element while the editor is displayed (defaults to false)
35202      */
35203     hideEl : false,
35204     /**
35205      * @cfg {String} cls
35206      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35207      */
35208     cls: "x-small-editor x-tree-editor",
35209     /**
35210      * @cfg {Boolean} shim
35211      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35212      */
35213     shim:false,
35214     // inherit
35215     shadow:"frame",
35216     /**
35217      * @cfg {Number} maxWidth
35218      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35219      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35220      * scroll and client offsets into account prior to each edit.
35221      */
35222     maxWidth: 250,
35223
35224     editDelay : 350,
35225
35226     // private
35227     fitToTree : function(ed, el){
35228         var td = this.tree.getTreeEl().dom, nd = el.dom;
35229         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35230             td.scrollLeft = nd.offsetLeft;
35231         }
35232         var w = Math.min(
35233                 this.maxWidth,
35234                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35235         this.setSize(w, '');
35236         
35237         return this.fireEvent('beforenodeedit', this, this.editNode);
35238         
35239     },
35240
35241     // private
35242     triggerEdit : function(node){
35243         this.completeEdit();
35244         this.editNode = node;
35245         this.startEdit(node.ui.textNode, node.text);
35246     },
35247
35248     // private
35249     bindScroll : function(){
35250         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35251     },
35252
35253     // private
35254     beforeNodeClick : function(node, e){
35255         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35256         this.lastClick = new Date();
35257         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35258             e.stopEvent();
35259             this.triggerEdit(node);
35260             return false;
35261         }
35262         return true;
35263     },
35264
35265     // private
35266     updateNode : function(ed, value){
35267         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35268         this.editNode.setText(value);
35269     },
35270
35271     // private
35272     onHide : function(){
35273         Roo.tree.TreeEditor.superclass.onHide.call(this);
35274         if(this.editNode){
35275             this.editNode.ui.focus();
35276         }
35277     },
35278
35279     // private
35280     onSpecialKey : function(field, e){
35281         var k = e.getKey();
35282         if(k == e.ESC){
35283             e.stopEvent();
35284             this.cancelEdit();
35285         }else if(k == e.ENTER && !e.hasModifier()){
35286             e.stopEvent();
35287             this.completeEdit();
35288         }
35289     }
35290 });//<Script type="text/javascript">
35291 /*
35292  * Based on:
35293  * Ext JS Library 1.1.1
35294  * Copyright(c) 2006-2007, Ext JS, LLC.
35295  *
35296  * Originally Released Under LGPL - original licence link has changed is not relivant.
35297  *
35298  * Fork - LGPL
35299  * <script type="text/javascript">
35300  */
35301  
35302 /**
35303  * Not documented??? - probably should be...
35304  */
35305
35306 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35307     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35308     
35309     renderElements : function(n, a, targetNode, bulkRender){
35310         //consel.log("renderElements?");
35311         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35312
35313         var t = n.getOwnerTree();
35314         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35315         
35316         var cols = t.columns;
35317         var bw = t.borderWidth;
35318         var c = cols[0];
35319         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35320          var cb = typeof a.checked == "boolean";
35321         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35322         var colcls = 'x-t-' + tid + '-c0';
35323         var buf = [
35324             '<li class="x-tree-node">',
35325             
35326                 
35327                 '<div class="x-tree-node-el ', a.cls,'">',
35328                     // extran...
35329                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35330                 
35331                 
35332                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35333                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35334                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35335                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35336                            (a.iconCls ? ' '+a.iconCls : ''),
35337                            '" unselectable="on" />',
35338                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35339                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35340                              
35341                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35342                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35343                             '<span unselectable="on" qtip="' + tx + '">',
35344                              tx,
35345                              '</span></a>' ,
35346                     '</div>',
35347                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35348                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35349                  ];
35350         for(var i = 1, len = cols.length; i < len; i++){
35351             c = cols[i];
35352             colcls = 'x-t-' + tid + '-c' +i;
35353             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35354             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35355                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35356                       "</div>");
35357          }
35358          
35359          buf.push(
35360             '</a>',
35361             '<div class="x-clear"></div></div>',
35362             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35363             "</li>");
35364         
35365         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35366             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35367                                 n.nextSibling.ui.getEl(), buf.join(""));
35368         }else{
35369             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35370         }
35371         var el = this.wrap.firstChild;
35372         this.elRow = el;
35373         this.elNode = el.firstChild;
35374         this.ranchor = el.childNodes[1];
35375         this.ctNode = this.wrap.childNodes[1];
35376         var cs = el.firstChild.childNodes;
35377         this.indentNode = cs[0];
35378         this.ecNode = cs[1];
35379         this.iconNode = cs[2];
35380         var index = 3;
35381         if(cb){
35382             this.checkbox = cs[3];
35383             index++;
35384         }
35385         this.anchor = cs[index];
35386         
35387         this.textNode = cs[index].firstChild;
35388         
35389         //el.on("click", this.onClick, this);
35390         //el.on("dblclick", this.onDblClick, this);
35391         
35392         
35393        // console.log(this);
35394     },
35395     initEvents : function(){
35396         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35397         
35398             
35399         var a = this.ranchor;
35400
35401         var el = Roo.get(a);
35402
35403         if(Roo.isOpera){ // opera render bug ignores the CSS
35404             el.setStyle("text-decoration", "none");
35405         }
35406
35407         el.on("click", this.onClick, this);
35408         el.on("dblclick", this.onDblClick, this);
35409         el.on("contextmenu", this.onContextMenu, this);
35410         
35411     },
35412     
35413     /*onSelectedChange : function(state){
35414         if(state){
35415             this.focus();
35416             this.addClass("x-tree-selected");
35417         }else{
35418             //this.blur();
35419             this.removeClass("x-tree-selected");
35420         }
35421     },*/
35422     addClass : function(cls){
35423         if(this.elRow){
35424             Roo.fly(this.elRow).addClass(cls);
35425         }
35426         
35427     },
35428     
35429     
35430     removeClass : function(cls){
35431         if(this.elRow){
35432             Roo.fly(this.elRow).removeClass(cls);
35433         }
35434     }
35435
35436     
35437     
35438 });//<Script type="text/javascript">
35439
35440 /*
35441  * Based on:
35442  * Ext JS Library 1.1.1
35443  * Copyright(c) 2006-2007, Ext JS, LLC.
35444  *
35445  * Originally Released Under LGPL - original licence link has changed is not relivant.
35446  *
35447  * Fork - LGPL
35448  * <script type="text/javascript">
35449  */
35450  
35451
35452 /**
35453  * @class Roo.tree.ColumnTree
35454  * @extends Roo.data.TreePanel
35455  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35456  * @cfg {int} borderWidth  compined right/left border allowance
35457  * @constructor
35458  * @param {String/HTMLElement/Element} el The container element
35459  * @param {Object} config
35460  */
35461 Roo.tree.ColumnTree =  function(el, config)
35462 {
35463    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35464    this.addEvents({
35465         /**
35466         * @event resize
35467         * Fire this event on a container when it resizes
35468         * @param {int} w Width
35469         * @param {int} h Height
35470         */
35471        "resize" : true
35472     });
35473     this.on('resize', this.onResize, this);
35474 };
35475
35476 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35477     //lines:false,
35478     
35479     
35480     borderWidth: Roo.isBorderBox ? 0 : 2, 
35481     headEls : false,
35482     
35483     render : function(){
35484         // add the header.....
35485        
35486         Roo.tree.ColumnTree.superclass.render.apply(this);
35487         
35488         this.el.addClass('x-column-tree');
35489         
35490         this.headers = this.el.createChild(
35491             {cls:'x-tree-headers'},this.innerCt.dom);
35492    
35493         var cols = this.columns, c;
35494         var totalWidth = 0;
35495         this.headEls = [];
35496         var  len = cols.length;
35497         for(var i = 0; i < len; i++){
35498              c = cols[i];
35499              totalWidth += c.width;
35500             this.headEls.push(this.headers.createChild({
35501                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35502                  cn: {
35503                      cls:'x-tree-hd-text',
35504                      html: c.header
35505                  },
35506                  style:'width:'+(c.width-this.borderWidth)+'px;'
35507              }));
35508         }
35509         this.headers.createChild({cls:'x-clear'});
35510         // prevent floats from wrapping when clipped
35511         this.headers.setWidth(totalWidth);
35512         //this.innerCt.setWidth(totalWidth);
35513         this.innerCt.setStyle({ overflow: 'auto' });
35514         this.onResize(this.width, this.height);
35515              
35516         
35517     },
35518     onResize : function(w,h)
35519     {
35520         this.height = h;
35521         this.width = w;
35522         // resize cols..
35523         this.innerCt.setWidth(this.width);
35524         this.innerCt.setHeight(this.height-20);
35525         
35526         // headers...
35527         var cols = this.columns, c;
35528         var totalWidth = 0;
35529         var expEl = false;
35530         var len = cols.length;
35531         for(var i = 0; i < len; i++){
35532             c = cols[i];
35533             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35534                 // it's the expander..
35535                 expEl  = this.headEls[i];
35536                 continue;
35537             }
35538             totalWidth += c.width;
35539             
35540         }
35541         if (expEl) {
35542             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35543         }
35544         this.headers.setWidth(w-20);
35545
35546         
35547         
35548         
35549     }
35550 });
35551 /*
35552  * Based on:
35553  * Ext JS Library 1.1.1
35554  * Copyright(c) 2006-2007, Ext JS, LLC.
35555  *
35556  * Originally Released Under LGPL - original licence link has changed is not relivant.
35557  *
35558  * Fork - LGPL
35559  * <script type="text/javascript">
35560  */
35561  
35562 /**
35563  * @class Roo.menu.Menu
35564  * @extends Roo.util.Observable
35565  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35566  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35567  * @constructor
35568  * Creates a new Menu
35569  * @param {Object} config Configuration options
35570  */
35571 Roo.menu.Menu = function(config){
35572     Roo.apply(this, config);
35573     this.id = this.id || Roo.id();
35574     this.addEvents({
35575         /**
35576          * @event beforeshow
35577          * Fires before this menu is displayed
35578          * @param {Roo.menu.Menu} this
35579          */
35580         beforeshow : true,
35581         /**
35582          * @event beforehide
35583          * Fires before this menu is hidden
35584          * @param {Roo.menu.Menu} this
35585          */
35586         beforehide : true,
35587         /**
35588          * @event show
35589          * Fires after this menu is displayed
35590          * @param {Roo.menu.Menu} this
35591          */
35592         show : true,
35593         /**
35594          * @event hide
35595          * Fires after this menu is hidden
35596          * @param {Roo.menu.Menu} this
35597          */
35598         hide : true,
35599         /**
35600          * @event click
35601          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35602          * @param {Roo.menu.Menu} this
35603          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35604          * @param {Roo.EventObject} e
35605          */
35606         click : true,
35607         /**
35608          * @event mouseover
35609          * Fires when the mouse is hovering over this menu
35610          * @param {Roo.menu.Menu} this
35611          * @param {Roo.EventObject} e
35612          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35613          */
35614         mouseover : true,
35615         /**
35616          * @event mouseout
35617          * Fires when the mouse exits this menu
35618          * @param {Roo.menu.Menu} this
35619          * @param {Roo.EventObject} e
35620          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35621          */
35622         mouseout : true,
35623         /**
35624          * @event itemclick
35625          * Fires when a menu item contained in this menu is clicked
35626          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35627          * @param {Roo.EventObject} e
35628          */
35629         itemclick: true
35630     });
35631     if (this.registerMenu) {
35632         Roo.menu.MenuMgr.register(this);
35633     }
35634     
35635     var mis = this.items;
35636     this.items = new Roo.util.MixedCollection();
35637     if(mis){
35638         this.add.apply(this, mis);
35639     }
35640 };
35641
35642 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35643     /**
35644      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35645      */
35646     minWidth : 120,
35647     /**
35648      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35649      * for bottom-right shadow (defaults to "sides")
35650      */
35651     shadow : "sides",
35652     /**
35653      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35654      * this menu (defaults to "tl-tr?")
35655      */
35656     subMenuAlign : "tl-tr?",
35657     /**
35658      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35659      * relative to its element of origin (defaults to "tl-bl?")
35660      */
35661     defaultAlign : "tl-bl?",
35662     /**
35663      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35664      */
35665     allowOtherMenus : false,
35666     /**
35667      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35668      */
35669     registerMenu : true,
35670
35671     hidden:true,
35672
35673     // private
35674     render : function(){
35675         if(this.el){
35676             return;
35677         }
35678         var el = this.el = new Roo.Layer({
35679             cls: "x-menu",
35680             shadow:this.shadow,
35681             constrain: false,
35682             parentEl: this.parentEl || document.body,
35683             zindex:15000
35684         });
35685
35686         this.keyNav = new Roo.menu.MenuNav(this);
35687
35688         if(this.plain){
35689             el.addClass("x-menu-plain");
35690         }
35691         if(this.cls){
35692             el.addClass(this.cls);
35693         }
35694         // generic focus element
35695         this.focusEl = el.createChild({
35696             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35697         });
35698         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35699         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35700         
35701         ul.on("mouseover", this.onMouseOver, this);
35702         ul.on("mouseout", this.onMouseOut, this);
35703         this.items.each(function(item){
35704             if (item.hidden) {
35705                 return;
35706             }
35707             
35708             var li = document.createElement("li");
35709             li.className = "x-menu-list-item";
35710             ul.dom.appendChild(li);
35711             item.render(li, this);
35712         }, this);
35713         this.ul = ul;
35714         this.autoWidth();
35715     },
35716
35717     // private
35718     autoWidth : function(){
35719         var el = this.el, ul = this.ul;
35720         if(!el){
35721             return;
35722         }
35723         var w = this.width;
35724         if(w){
35725             el.setWidth(w);
35726         }else if(Roo.isIE){
35727             el.setWidth(this.minWidth);
35728             var t = el.dom.offsetWidth; // force recalc
35729             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35730         }
35731     },
35732
35733     // private
35734     delayAutoWidth : function(){
35735         if(this.rendered){
35736             if(!this.awTask){
35737                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35738             }
35739             this.awTask.delay(20);
35740         }
35741     },
35742
35743     // private
35744     findTargetItem : function(e){
35745         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35746         if(t && t.menuItemId){
35747             return this.items.get(t.menuItemId);
35748         }
35749     },
35750
35751     // private
35752     onClick : function(e){
35753         Roo.log("menu.onClick");
35754         var t = this.findTargetItem(e);
35755         if(!t){
35756             return;
35757         }
35758         Roo.log(e);
35759         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35760             if(t == this.activeItem && t.shouldDeactivate(e)){
35761                 this.activeItem.deactivate();
35762                 delete this.activeItem;
35763                 return;
35764             }
35765             if(t.canActivate){
35766                 this.setActiveItem(t, true);
35767             }
35768             return;
35769             
35770             
35771         }
35772         
35773         t.onClick(e);
35774         this.fireEvent("click", this, t, e);
35775     },
35776
35777     // private
35778     setActiveItem : function(item, autoExpand){
35779         if(item != this.activeItem){
35780             if(this.activeItem){
35781                 this.activeItem.deactivate();
35782             }
35783             this.activeItem = item;
35784             item.activate(autoExpand);
35785         }else if(autoExpand){
35786             item.expandMenu();
35787         }
35788     },
35789
35790     // private
35791     tryActivate : function(start, step){
35792         var items = this.items;
35793         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35794             var item = items.get(i);
35795             if(!item.disabled && item.canActivate){
35796                 this.setActiveItem(item, false);
35797                 return item;
35798             }
35799         }
35800         return false;
35801     },
35802
35803     // private
35804     onMouseOver : function(e){
35805         var t;
35806         if(t = this.findTargetItem(e)){
35807             if(t.canActivate && !t.disabled){
35808                 this.setActiveItem(t, true);
35809             }
35810         }
35811         this.fireEvent("mouseover", this, e, t);
35812     },
35813
35814     // private
35815     onMouseOut : function(e){
35816         var t;
35817         if(t = this.findTargetItem(e)){
35818             if(t == this.activeItem && t.shouldDeactivate(e)){
35819                 this.activeItem.deactivate();
35820                 delete this.activeItem;
35821             }
35822         }
35823         this.fireEvent("mouseout", this, e, t);
35824     },
35825
35826     /**
35827      * Read-only.  Returns true if the menu is currently displayed, else false.
35828      * @type Boolean
35829      */
35830     isVisible : function(){
35831         return this.el && !this.hidden;
35832     },
35833
35834     /**
35835      * Displays this menu relative to another element
35836      * @param {String/HTMLElement/Roo.Element} element The element to align to
35837      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35838      * the element (defaults to this.defaultAlign)
35839      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35840      */
35841     show : function(el, pos, parentMenu){
35842         this.parentMenu = parentMenu;
35843         if(!this.el){
35844             this.render();
35845         }
35846         this.fireEvent("beforeshow", this);
35847         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35848     },
35849
35850     /**
35851      * Displays this menu at a specific xy position
35852      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35853      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35854      */
35855     showAt : function(xy, parentMenu, /* private: */_e){
35856         this.parentMenu = parentMenu;
35857         if(!this.el){
35858             this.render();
35859         }
35860         if(_e !== false){
35861             this.fireEvent("beforeshow", this);
35862             xy = this.el.adjustForConstraints(xy);
35863         }
35864         this.el.setXY(xy);
35865         this.el.show();
35866         this.hidden = false;
35867         this.focus();
35868         this.fireEvent("show", this);
35869     },
35870
35871     focus : function(){
35872         if(!this.hidden){
35873             this.doFocus.defer(50, this);
35874         }
35875     },
35876
35877     doFocus : function(){
35878         if(!this.hidden){
35879             this.focusEl.focus();
35880         }
35881     },
35882
35883     /**
35884      * Hides this menu and optionally all parent menus
35885      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35886      */
35887     hide : function(deep){
35888         if(this.el && this.isVisible()){
35889             this.fireEvent("beforehide", this);
35890             if(this.activeItem){
35891                 this.activeItem.deactivate();
35892                 this.activeItem = null;
35893             }
35894             this.el.hide();
35895             this.hidden = true;
35896             this.fireEvent("hide", this);
35897         }
35898         if(deep === true && this.parentMenu){
35899             this.parentMenu.hide(true);
35900         }
35901     },
35902
35903     /**
35904      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35905      * Any of the following are valid:
35906      * <ul>
35907      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35908      * <li>An HTMLElement object which will be converted to a menu item</li>
35909      * <li>A menu item config object that will be created as a new menu item</li>
35910      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35911      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35912      * </ul>
35913      * Usage:
35914      * <pre><code>
35915 // Create the menu
35916 var menu = new Roo.menu.Menu();
35917
35918 // Create a menu item to add by reference
35919 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35920
35921 // Add a bunch of items at once using different methods.
35922 // Only the last item added will be returned.
35923 var item = menu.add(
35924     menuItem,                // add existing item by ref
35925     'Dynamic Item',          // new TextItem
35926     '-',                     // new separator
35927     { text: 'Config Item' }  // new item by config
35928 );
35929 </code></pre>
35930      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35931      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35932      */
35933     add : function(){
35934         var a = arguments, l = a.length, item;
35935         for(var i = 0; i < l; i++){
35936             var el = a[i];
35937             if ((typeof(el) == "object") && el.xtype && el.xns) {
35938                 el = Roo.factory(el, Roo.menu);
35939             }
35940             
35941             if(el.render){ // some kind of Item
35942                 item = this.addItem(el);
35943             }else if(typeof el == "string"){ // string
35944                 if(el == "separator" || el == "-"){
35945                     item = this.addSeparator();
35946                 }else{
35947                     item = this.addText(el);
35948                 }
35949             }else if(el.tagName || el.el){ // element
35950                 item = this.addElement(el);
35951             }else if(typeof el == "object"){ // must be menu item config?
35952                 item = this.addMenuItem(el);
35953             }
35954         }
35955         return item;
35956     },
35957
35958     /**
35959      * Returns this menu's underlying {@link Roo.Element} object
35960      * @return {Roo.Element} The element
35961      */
35962     getEl : function(){
35963         if(!this.el){
35964             this.render();
35965         }
35966         return this.el;
35967     },
35968
35969     /**
35970      * Adds a separator bar to the menu
35971      * @return {Roo.menu.Item} The menu item that was added
35972      */
35973     addSeparator : function(){
35974         return this.addItem(new Roo.menu.Separator());
35975     },
35976
35977     /**
35978      * Adds an {@link Roo.Element} object to the menu
35979      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35980      * @return {Roo.menu.Item} The menu item that was added
35981      */
35982     addElement : function(el){
35983         return this.addItem(new Roo.menu.BaseItem(el));
35984     },
35985
35986     /**
35987      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35988      * @param {Roo.menu.Item} item The menu item to add
35989      * @return {Roo.menu.Item} The menu item that was added
35990      */
35991     addItem : function(item){
35992         this.items.add(item);
35993         if(this.ul){
35994             var li = document.createElement("li");
35995             li.className = "x-menu-list-item";
35996             this.ul.dom.appendChild(li);
35997             item.render(li, this);
35998             this.delayAutoWidth();
35999         }
36000         return item;
36001     },
36002
36003     /**
36004      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
36005      * @param {Object} config A MenuItem config object
36006      * @return {Roo.menu.Item} The menu item that was added
36007      */
36008     addMenuItem : function(config){
36009         if(!(config instanceof Roo.menu.Item)){
36010             if(typeof config.checked == "boolean"){ // must be check menu item config?
36011                 config = new Roo.menu.CheckItem(config);
36012             }else{
36013                 config = new Roo.menu.Item(config);
36014             }
36015         }
36016         return this.addItem(config);
36017     },
36018
36019     /**
36020      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36021      * @param {String} text The text to display in the menu item
36022      * @return {Roo.menu.Item} The menu item that was added
36023      */
36024     addText : function(text){
36025         return this.addItem(new Roo.menu.TextItem({ text : text }));
36026     },
36027
36028     /**
36029      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36030      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36031      * @param {Roo.menu.Item} item The menu item to add
36032      * @return {Roo.menu.Item} The menu item that was added
36033      */
36034     insert : function(index, item){
36035         this.items.insert(index, item);
36036         if(this.ul){
36037             var li = document.createElement("li");
36038             li.className = "x-menu-list-item";
36039             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36040             item.render(li, this);
36041             this.delayAutoWidth();
36042         }
36043         return item;
36044     },
36045
36046     /**
36047      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36048      * @param {Roo.menu.Item} item The menu item to remove
36049      */
36050     remove : function(item){
36051         this.items.removeKey(item.id);
36052         item.destroy();
36053     },
36054
36055     /**
36056      * Removes and destroys all items in the menu
36057      */
36058     removeAll : function(){
36059         var f;
36060         while(f = this.items.first()){
36061             this.remove(f);
36062         }
36063     }
36064 });
36065
36066 // MenuNav is a private utility class used internally by the Menu
36067 Roo.menu.MenuNav = function(menu){
36068     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36069     this.scope = this.menu = menu;
36070 };
36071
36072 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36073     doRelay : function(e, h){
36074         var k = e.getKey();
36075         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36076             this.menu.tryActivate(0, 1);
36077             return false;
36078         }
36079         return h.call(this.scope || this, e, this.menu);
36080     },
36081
36082     up : function(e, m){
36083         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36084             m.tryActivate(m.items.length-1, -1);
36085         }
36086     },
36087
36088     down : function(e, m){
36089         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36090             m.tryActivate(0, 1);
36091         }
36092     },
36093
36094     right : function(e, m){
36095         if(m.activeItem){
36096             m.activeItem.expandMenu(true);
36097         }
36098     },
36099
36100     left : function(e, m){
36101         m.hide();
36102         if(m.parentMenu && m.parentMenu.activeItem){
36103             m.parentMenu.activeItem.activate();
36104         }
36105     },
36106
36107     enter : function(e, m){
36108         if(m.activeItem){
36109             e.stopPropagation();
36110             m.activeItem.onClick(e);
36111             m.fireEvent("click", this, m.activeItem);
36112             return true;
36113         }
36114     }
36115 });/*
36116  * Based on:
36117  * Ext JS Library 1.1.1
36118  * Copyright(c) 2006-2007, Ext JS, LLC.
36119  *
36120  * Originally Released Under LGPL - original licence link has changed is not relivant.
36121  *
36122  * Fork - LGPL
36123  * <script type="text/javascript">
36124  */
36125  
36126 /**
36127  * @class Roo.menu.MenuMgr
36128  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36129  * @singleton
36130  */
36131 Roo.menu.MenuMgr = function(){
36132    var menus, active, groups = {}, attached = false, lastShow = new Date();
36133
36134    // private - called when first menu is created
36135    function init(){
36136        menus = {};
36137        active = new Roo.util.MixedCollection();
36138        Roo.get(document).addKeyListener(27, function(){
36139            if(active.length > 0){
36140                hideAll();
36141            }
36142        });
36143    }
36144
36145    // private
36146    function hideAll(){
36147        if(active && active.length > 0){
36148            var c = active.clone();
36149            c.each(function(m){
36150                m.hide();
36151            });
36152        }
36153    }
36154
36155    // private
36156    function onHide(m){
36157        active.remove(m);
36158        if(active.length < 1){
36159            Roo.get(document).un("mousedown", onMouseDown);
36160            attached = false;
36161        }
36162    }
36163
36164    // private
36165    function onShow(m){
36166        var last = active.last();
36167        lastShow = new Date();
36168        active.add(m);
36169        if(!attached){
36170            Roo.get(document).on("mousedown", onMouseDown);
36171            attached = true;
36172        }
36173        if(m.parentMenu){
36174           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36175           m.parentMenu.activeChild = m;
36176        }else if(last && last.isVisible()){
36177           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36178        }
36179    }
36180
36181    // private
36182    function onBeforeHide(m){
36183        if(m.activeChild){
36184            m.activeChild.hide();
36185        }
36186        if(m.autoHideTimer){
36187            clearTimeout(m.autoHideTimer);
36188            delete m.autoHideTimer;
36189        }
36190    }
36191
36192    // private
36193    function onBeforeShow(m){
36194        var pm = m.parentMenu;
36195        if(!pm && !m.allowOtherMenus){
36196            hideAll();
36197        }else if(pm && pm.activeChild && active != m){
36198            pm.activeChild.hide();
36199        }
36200    }
36201
36202    // private
36203    function onMouseDown(e){
36204        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36205            hideAll();
36206        }
36207    }
36208
36209    // private
36210    function onBeforeCheck(mi, state){
36211        if(state){
36212            var g = groups[mi.group];
36213            for(var i = 0, l = g.length; i < l; i++){
36214                if(g[i] != mi){
36215                    g[i].setChecked(false);
36216                }
36217            }
36218        }
36219    }
36220
36221    return {
36222
36223        /**
36224         * Hides all menus that are currently visible
36225         */
36226        hideAll : function(){
36227             hideAll();  
36228        },
36229
36230        // private
36231        register : function(menu){
36232            if(!menus){
36233                init();
36234            }
36235            menus[menu.id] = menu;
36236            menu.on("beforehide", onBeforeHide);
36237            menu.on("hide", onHide);
36238            menu.on("beforeshow", onBeforeShow);
36239            menu.on("show", onShow);
36240            var g = menu.group;
36241            if(g && menu.events["checkchange"]){
36242                if(!groups[g]){
36243                    groups[g] = [];
36244                }
36245                groups[g].push(menu);
36246                menu.on("checkchange", onCheck);
36247            }
36248        },
36249
36250         /**
36251          * Returns a {@link Roo.menu.Menu} object
36252          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36253          * be used to generate and return a new Menu instance.
36254          */
36255        get : function(menu){
36256            if(typeof menu == "string"){ // menu id
36257                return menus[menu];
36258            }else if(menu.events){  // menu instance
36259                return menu;
36260            }else if(typeof menu.length == 'number'){ // array of menu items?
36261                return new Roo.menu.Menu({items:menu});
36262            }else{ // otherwise, must be a config
36263                return new Roo.menu.Menu(menu);
36264            }
36265        },
36266
36267        // private
36268        unregister : function(menu){
36269            delete menus[menu.id];
36270            menu.un("beforehide", onBeforeHide);
36271            menu.un("hide", onHide);
36272            menu.un("beforeshow", onBeforeShow);
36273            menu.un("show", onShow);
36274            var g = menu.group;
36275            if(g && menu.events["checkchange"]){
36276                groups[g].remove(menu);
36277                menu.un("checkchange", onCheck);
36278            }
36279        },
36280
36281        // private
36282        registerCheckable : function(menuItem){
36283            var g = menuItem.group;
36284            if(g){
36285                if(!groups[g]){
36286                    groups[g] = [];
36287                }
36288                groups[g].push(menuItem);
36289                menuItem.on("beforecheckchange", onBeforeCheck);
36290            }
36291        },
36292
36293        // private
36294        unregisterCheckable : function(menuItem){
36295            var g = menuItem.group;
36296            if(g){
36297                groups[g].remove(menuItem);
36298                menuItem.un("beforecheckchange", onBeforeCheck);
36299            }
36300        }
36301    };
36302 }();/*
36303  * Based on:
36304  * Ext JS Library 1.1.1
36305  * Copyright(c) 2006-2007, Ext JS, LLC.
36306  *
36307  * Originally Released Under LGPL - original licence link has changed is not relivant.
36308  *
36309  * Fork - LGPL
36310  * <script type="text/javascript">
36311  */
36312  
36313
36314 /**
36315  * @class Roo.menu.BaseItem
36316  * @extends Roo.Component
36317  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36318  * management and base configuration options shared by all menu components.
36319  * @constructor
36320  * Creates a new BaseItem
36321  * @param {Object} config Configuration options
36322  */
36323 Roo.menu.BaseItem = function(config){
36324     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36325
36326     this.addEvents({
36327         /**
36328          * @event click
36329          * Fires when this item is clicked
36330          * @param {Roo.menu.BaseItem} this
36331          * @param {Roo.EventObject} e
36332          */
36333         click: true,
36334         /**
36335          * @event activate
36336          * Fires when this item is activated
36337          * @param {Roo.menu.BaseItem} this
36338          */
36339         activate : true,
36340         /**
36341          * @event deactivate
36342          * Fires when this item is deactivated
36343          * @param {Roo.menu.BaseItem} this
36344          */
36345         deactivate : true
36346     });
36347
36348     if(this.handler){
36349         this.on("click", this.handler, this.scope, true);
36350     }
36351 };
36352
36353 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36354     /**
36355      * @cfg {Function} handler
36356      * A function that will handle the click event of this menu item (defaults to undefined)
36357      */
36358     /**
36359      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36360      */
36361     canActivate : false,
36362     
36363      /**
36364      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36365      */
36366     hidden: false,
36367     
36368     /**
36369      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36370      */
36371     activeClass : "x-menu-item-active",
36372     /**
36373      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36374      */
36375     hideOnClick : true,
36376     /**
36377      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36378      */
36379     hideDelay : 100,
36380
36381     // private
36382     ctype: "Roo.menu.BaseItem",
36383
36384     // private
36385     actionMode : "container",
36386
36387     // private
36388     render : function(container, parentMenu){
36389         this.parentMenu = parentMenu;
36390         Roo.menu.BaseItem.superclass.render.call(this, container);
36391         this.container.menuItemId = this.id;
36392     },
36393
36394     // private
36395     onRender : function(container, position){
36396         this.el = Roo.get(this.el);
36397         container.dom.appendChild(this.el.dom);
36398     },
36399
36400     // private
36401     onClick : function(e){
36402         if(!this.disabled && this.fireEvent("click", this, e) !== false
36403                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36404             this.handleClick(e);
36405         }else{
36406             e.stopEvent();
36407         }
36408     },
36409
36410     // private
36411     activate : function(){
36412         if(this.disabled){
36413             return false;
36414         }
36415         var li = this.container;
36416         li.addClass(this.activeClass);
36417         this.region = li.getRegion().adjust(2, 2, -2, -2);
36418         this.fireEvent("activate", this);
36419         return true;
36420     },
36421
36422     // private
36423     deactivate : function(){
36424         this.container.removeClass(this.activeClass);
36425         this.fireEvent("deactivate", this);
36426     },
36427
36428     // private
36429     shouldDeactivate : function(e){
36430         return !this.region || !this.region.contains(e.getPoint());
36431     },
36432
36433     // private
36434     handleClick : function(e){
36435         if(this.hideOnClick){
36436             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36437         }
36438     },
36439
36440     // private
36441     expandMenu : function(autoActivate){
36442         // do nothing
36443     },
36444
36445     // private
36446     hideMenu : function(){
36447         // do nothing
36448     }
36449 });/*
36450  * Based on:
36451  * Ext JS Library 1.1.1
36452  * Copyright(c) 2006-2007, Ext JS, LLC.
36453  *
36454  * Originally Released Under LGPL - original licence link has changed is not relivant.
36455  *
36456  * Fork - LGPL
36457  * <script type="text/javascript">
36458  */
36459  
36460 /**
36461  * @class Roo.menu.Adapter
36462  * @extends Roo.menu.BaseItem
36463  * 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.
36464  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36465  * @constructor
36466  * Creates a new Adapter
36467  * @param {Object} config Configuration options
36468  */
36469 Roo.menu.Adapter = function(component, config){
36470     Roo.menu.Adapter.superclass.constructor.call(this, config);
36471     this.component = component;
36472 };
36473 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36474     // private
36475     canActivate : true,
36476
36477     // private
36478     onRender : function(container, position){
36479         this.component.render(container);
36480         this.el = this.component.getEl();
36481     },
36482
36483     // private
36484     activate : function(){
36485         if(this.disabled){
36486             return false;
36487         }
36488         this.component.focus();
36489         this.fireEvent("activate", this);
36490         return true;
36491     },
36492
36493     // private
36494     deactivate : function(){
36495         this.fireEvent("deactivate", this);
36496     },
36497
36498     // private
36499     disable : function(){
36500         this.component.disable();
36501         Roo.menu.Adapter.superclass.disable.call(this);
36502     },
36503
36504     // private
36505     enable : function(){
36506         this.component.enable();
36507         Roo.menu.Adapter.superclass.enable.call(this);
36508     }
36509 });/*
36510  * Based on:
36511  * Ext JS Library 1.1.1
36512  * Copyright(c) 2006-2007, Ext JS, LLC.
36513  *
36514  * Originally Released Under LGPL - original licence link has changed is not relivant.
36515  *
36516  * Fork - LGPL
36517  * <script type="text/javascript">
36518  */
36519
36520 /**
36521  * @class Roo.menu.TextItem
36522  * @extends Roo.menu.BaseItem
36523  * Adds a static text string to a menu, usually used as either a heading or group separator.
36524  * Note: old style constructor with text is still supported.
36525  * 
36526  * @constructor
36527  * Creates a new TextItem
36528  * @param {Object} cfg Configuration
36529  */
36530 Roo.menu.TextItem = function(cfg){
36531     if (typeof(cfg) == 'string') {
36532         this.text = cfg;
36533     } else {
36534         Roo.apply(this,cfg);
36535     }
36536     
36537     Roo.menu.TextItem.superclass.constructor.call(this);
36538 };
36539
36540 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36541     /**
36542      * @cfg {Boolean} text Text to show on item.
36543      */
36544     text : '',
36545     
36546     /**
36547      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36548      */
36549     hideOnClick : false,
36550     /**
36551      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36552      */
36553     itemCls : "x-menu-text",
36554
36555     // private
36556     onRender : function(){
36557         var s = document.createElement("span");
36558         s.className = this.itemCls;
36559         s.innerHTML = this.text;
36560         this.el = s;
36561         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36562     }
36563 });/*
36564  * Based on:
36565  * Ext JS Library 1.1.1
36566  * Copyright(c) 2006-2007, Ext JS, LLC.
36567  *
36568  * Originally Released Under LGPL - original licence link has changed is not relivant.
36569  *
36570  * Fork - LGPL
36571  * <script type="text/javascript">
36572  */
36573
36574 /**
36575  * @class Roo.menu.Separator
36576  * @extends Roo.menu.BaseItem
36577  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36578  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36579  * @constructor
36580  * @param {Object} config Configuration options
36581  */
36582 Roo.menu.Separator = function(config){
36583     Roo.menu.Separator.superclass.constructor.call(this, config);
36584 };
36585
36586 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36587     /**
36588      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36589      */
36590     itemCls : "x-menu-sep",
36591     /**
36592      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36593      */
36594     hideOnClick : false,
36595
36596     // private
36597     onRender : function(li){
36598         var s = document.createElement("span");
36599         s.className = this.itemCls;
36600         s.innerHTML = "&#160;";
36601         this.el = s;
36602         li.addClass("x-menu-sep-li");
36603         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36604     }
36605 });/*
36606  * Based on:
36607  * Ext JS Library 1.1.1
36608  * Copyright(c) 2006-2007, Ext JS, LLC.
36609  *
36610  * Originally Released Under LGPL - original licence link has changed is not relivant.
36611  *
36612  * Fork - LGPL
36613  * <script type="text/javascript">
36614  */
36615 /**
36616  * @class Roo.menu.Item
36617  * @extends Roo.menu.BaseItem
36618  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36619  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36620  * activation and click handling.
36621  * @constructor
36622  * Creates a new Item
36623  * @param {Object} config Configuration options
36624  */
36625 Roo.menu.Item = function(config){
36626     Roo.menu.Item.superclass.constructor.call(this, config);
36627     if(this.menu){
36628         this.menu = Roo.menu.MenuMgr.get(this.menu);
36629     }
36630 };
36631 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36632     
36633     /**
36634      * @cfg {String} text
36635      * The text to show on the menu item.
36636      */
36637     text: '',
36638      /**
36639      * @cfg {String} HTML to render in menu
36640      * The text to show on the menu item (HTML version).
36641      */
36642     html: '',
36643     /**
36644      * @cfg {String} icon
36645      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36646      */
36647     icon: undefined,
36648     /**
36649      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36650      */
36651     itemCls : "x-menu-item",
36652     /**
36653      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36654      */
36655     canActivate : true,
36656     /**
36657      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36658      */
36659     showDelay: 200,
36660     // doc'd in BaseItem
36661     hideDelay: 200,
36662
36663     // private
36664     ctype: "Roo.menu.Item",
36665     
36666     // private
36667     onRender : function(container, position){
36668         var el = document.createElement("a");
36669         el.hideFocus = true;
36670         el.unselectable = "on";
36671         el.href = this.href || "#";
36672         if(this.hrefTarget){
36673             el.target = this.hrefTarget;
36674         }
36675         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36676         
36677         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36678         
36679         el.innerHTML = String.format(
36680                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36681                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36682         this.el = el;
36683         Roo.menu.Item.superclass.onRender.call(this, container, position);
36684     },
36685
36686     /**
36687      * Sets the text to display in this menu item
36688      * @param {String} text The text to display
36689      * @param {Boolean} isHTML true to indicate text is pure html.
36690      */
36691     setText : function(text, isHTML){
36692         if (isHTML) {
36693             this.html = text;
36694         } else {
36695             this.text = text;
36696             this.html = '';
36697         }
36698         if(this.rendered){
36699             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36700      
36701             this.el.update(String.format(
36702                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36703                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36704             this.parentMenu.autoWidth();
36705         }
36706     },
36707
36708     // private
36709     handleClick : function(e){
36710         if(!this.href){ // if no link defined, stop the event automatically
36711             e.stopEvent();
36712         }
36713         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36714     },
36715
36716     // private
36717     activate : function(autoExpand){
36718         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36719             this.focus();
36720             if(autoExpand){
36721                 this.expandMenu();
36722             }
36723         }
36724         return true;
36725     },
36726
36727     // private
36728     shouldDeactivate : function(e){
36729         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36730             if(this.menu && this.menu.isVisible()){
36731                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36732             }
36733             return true;
36734         }
36735         return false;
36736     },
36737
36738     // private
36739     deactivate : function(){
36740         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36741         this.hideMenu();
36742     },
36743
36744     // private
36745     expandMenu : function(autoActivate){
36746         if(!this.disabled && this.menu){
36747             clearTimeout(this.hideTimer);
36748             delete this.hideTimer;
36749             if(!this.menu.isVisible() && !this.showTimer){
36750                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36751             }else if (this.menu.isVisible() && autoActivate){
36752                 this.menu.tryActivate(0, 1);
36753             }
36754         }
36755     },
36756
36757     // private
36758     deferExpand : function(autoActivate){
36759         delete this.showTimer;
36760         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36761         if(autoActivate){
36762             this.menu.tryActivate(0, 1);
36763         }
36764     },
36765
36766     // private
36767     hideMenu : function(){
36768         clearTimeout(this.showTimer);
36769         delete this.showTimer;
36770         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36771             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36772         }
36773     },
36774
36775     // private
36776     deferHide : function(){
36777         delete this.hideTimer;
36778         this.menu.hide();
36779     }
36780 });/*
36781  * Based on:
36782  * Ext JS Library 1.1.1
36783  * Copyright(c) 2006-2007, Ext JS, LLC.
36784  *
36785  * Originally Released Under LGPL - original licence link has changed is not relivant.
36786  *
36787  * Fork - LGPL
36788  * <script type="text/javascript">
36789  */
36790  
36791 /**
36792  * @class Roo.menu.CheckItem
36793  * @extends Roo.menu.Item
36794  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36795  * @constructor
36796  * Creates a new CheckItem
36797  * @param {Object} config Configuration options
36798  */
36799 Roo.menu.CheckItem = function(config){
36800     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36801     this.addEvents({
36802         /**
36803          * @event beforecheckchange
36804          * Fires before the checked value is set, providing an opportunity to cancel if needed
36805          * @param {Roo.menu.CheckItem} this
36806          * @param {Boolean} checked The new checked value that will be set
36807          */
36808         "beforecheckchange" : true,
36809         /**
36810          * @event checkchange
36811          * Fires after the checked value has been set
36812          * @param {Roo.menu.CheckItem} this
36813          * @param {Boolean} checked The checked value that was set
36814          */
36815         "checkchange" : true
36816     });
36817     if(this.checkHandler){
36818         this.on('checkchange', this.checkHandler, this.scope);
36819     }
36820 };
36821 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36822     /**
36823      * @cfg {String} group
36824      * All check items with the same group name will automatically be grouped into a single-select
36825      * radio button group (defaults to '')
36826      */
36827     /**
36828      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36829      */
36830     itemCls : "x-menu-item x-menu-check-item",
36831     /**
36832      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36833      */
36834     groupClass : "x-menu-group-item",
36835
36836     /**
36837      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36838      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36839      * initialized with checked = true will be rendered as checked.
36840      */
36841     checked: false,
36842
36843     // private
36844     ctype: "Roo.menu.CheckItem",
36845
36846     // private
36847     onRender : function(c){
36848         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36849         if(this.group){
36850             this.el.addClass(this.groupClass);
36851         }
36852         Roo.menu.MenuMgr.registerCheckable(this);
36853         if(this.checked){
36854             this.checked = false;
36855             this.setChecked(true, true);
36856         }
36857     },
36858
36859     // private
36860     destroy : function(){
36861         if(this.rendered){
36862             Roo.menu.MenuMgr.unregisterCheckable(this);
36863         }
36864         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36865     },
36866
36867     /**
36868      * Set the checked state of this item
36869      * @param {Boolean} checked The new checked value
36870      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36871      */
36872     setChecked : function(state, suppressEvent){
36873         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36874             if(this.container){
36875                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36876             }
36877             this.checked = state;
36878             if(suppressEvent !== true){
36879                 this.fireEvent("checkchange", this, state);
36880             }
36881         }
36882     },
36883
36884     // private
36885     handleClick : function(e){
36886        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36887            this.setChecked(!this.checked);
36888        }
36889        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36890     }
36891 });/*
36892  * Based on:
36893  * Ext JS Library 1.1.1
36894  * Copyright(c) 2006-2007, Ext JS, LLC.
36895  *
36896  * Originally Released Under LGPL - original licence link has changed is not relivant.
36897  *
36898  * Fork - LGPL
36899  * <script type="text/javascript">
36900  */
36901  
36902 /**
36903  * @class Roo.menu.DateItem
36904  * @extends Roo.menu.Adapter
36905  * A menu item that wraps the {@link Roo.DatPicker} component.
36906  * @constructor
36907  * Creates a new DateItem
36908  * @param {Object} config Configuration options
36909  */
36910 Roo.menu.DateItem = function(config){
36911     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36912     /** The Roo.DatePicker object @type Roo.DatePicker */
36913     this.picker = this.component;
36914     this.addEvents({select: true});
36915     
36916     this.picker.on("render", function(picker){
36917         picker.getEl().swallowEvent("click");
36918         picker.container.addClass("x-menu-date-item");
36919     });
36920
36921     this.picker.on("select", this.onSelect, this);
36922 };
36923
36924 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36925     // private
36926     onSelect : function(picker, date){
36927         this.fireEvent("select", this, date, picker);
36928         Roo.menu.DateItem.superclass.handleClick.call(this);
36929     }
36930 });/*
36931  * Based on:
36932  * Ext JS Library 1.1.1
36933  * Copyright(c) 2006-2007, Ext JS, LLC.
36934  *
36935  * Originally Released Under LGPL - original licence link has changed is not relivant.
36936  *
36937  * Fork - LGPL
36938  * <script type="text/javascript">
36939  */
36940  
36941 /**
36942  * @class Roo.menu.ColorItem
36943  * @extends Roo.menu.Adapter
36944  * A menu item that wraps the {@link Roo.ColorPalette} component.
36945  * @constructor
36946  * Creates a new ColorItem
36947  * @param {Object} config Configuration options
36948  */
36949 Roo.menu.ColorItem = function(config){
36950     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36951     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36952     this.palette = this.component;
36953     this.relayEvents(this.palette, ["select"]);
36954     if(this.selectHandler){
36955         this.on('select', this.selectHandler, this.scope);
36956     }
36957 };
36958 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36959  * Based on:
36960  * Ext JS Library 1.1.1
36961  * Copyright(c) 2006-2007, Ext JS, LLC.
36962  *
36963  * Originally Released Under LGPL - original licence link has changed is not relivant.
36964  *
36965  * Fork - LGPL
36966  * <script type="text/javascript">
36967  */
36968  
36969
36970 /**
36971  * @class Roo.menu.DateMenu
36972  * @extends Roo.menu.Menu
36973  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36974  * @constructor
36975  * Creates a new DateMenu
36976  * @param {Object} config Configuration options
36977  */
36978 Roo.menu.DateMenu = function(config){
36979     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36980     this.plain = true;
36981     var di = new Roo.menu.DateItem(config);
36982     this.add(di);
36983     /**
36984      * The {@link Roo.DatePicker} instance for this DateMenu
36985      * @type DatePicker
36986      */
36987     this.picker = di.picker;
36988     /**
36989      * @event select
36990      * @param {DatePicker} picker
36991      * @param {Date} date
36992      */
36993     this.relayEvents(di, ["select"]);
36994     this.on('beforeshow', function(){
36995         if(this.picker){
36996             this.picker.hideMonthPicker(false);
36997         }
36998     }, this);
36999 };
37000 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
37001     cls:'x-date-menu'
37002 });/*
37003  * Based on:
37004  * Ext JS Library 1.1.1
37005  * Copyright(c) 2006-2007, Ext JS, LLC.
37006  *
37007  * Originally Released Under LGPL - original licence link has changed is not relivant.
37008  *
37009  * Fork - LGPL
37010  * <script type="text/javascript">
37011  */
37012  
37013
37014 /**
37015  * @class Roo.menu.ColorMenu
37016  * @extends Roo.menu.Menu
37017  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37018  * @constructor
37019  * Creates a new ColorMenu
37020  * @param {Object} config Configuration options
37021  */
37022 Roo.menu.ColorMenu = function(config){
37023     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37024     this.plain = true;
37025     var ci = new Roo.menu.ColorItem(config);
37026     this.add(ci);
37027     /**
37028      * The {@link Roo.ColorPalette} instance for this ColorMenu
37029      * @type ColorPalette
37030      */
37031     this.palette = ci.palette;
37032     /**
37033      * @event select
37034      * @param {ColorPalette} palette
37035      * @param {String} color
37036      */
37037     this.relayEvents(ci, ["select"]);
37038 };
37039 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37040  * Based on:
37041  * Ext JS Library 1.1.1
37042  * Copyright(c) 2006-2007, Ext JS, LLC.
37043  *
37044  * Originally Released Under LGPL - original licence link has changed is not relivant.
37045  *
37046  * Fork - LGPL
37047  * <script type="text/javascript">
37048  */
37049  
37050 /**
37051  * @class Roo.form.Field
37052  * @extends Roo.BoxComponent
37053  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37054  * @constructor
37055  * Creates a new Field
37056  * @param {Object} config Configuration options
37057  */
37058 Roo.form.Field = function(config){
37059     Roo.form.Field.superclass.constructor.call(this, config);
37060 };
37061
37062 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37063     /**
37064      * @cfg {String} fieldLabel Label to use when rendering a form.
37065      */
37066        /**
37067      * @cfg {String} qtip Mouse over tip
37068      */
37069      
37070     /**
37071      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37072      */
37073     invalidClass : "x-form-invalid",
37074     /**
37075      * @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")
37076      */
37077     invalidText : "The value in this field is invalid",
37078     /**
37079      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37080      */
37081     focusClass : "x-form-focus",
37082     /**
37083      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37084       automatic validation (defaults to "keyup").
37085      */
37086     validationEvent : "keyup",
37087     /**
37088      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37089      */
37090     validateOnBlur : true,
37091     /**
37092      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37093      */
37094     validationDelay : 250,
37095     /**
37096      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37097      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37098      */
37099     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37100     /**
37101      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37102      */
37103     fieldClass : "x-form-field",
37104     /**
37105      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37106      *<pre>
37107 Value         Description
37108 -----------   ----------------------------------------------------------------------
37109 qtip          Display a quick tip when the user hovers over the field
37110 title         Display a default browser title attribute popup
37111 under         Add a block div beneath the field containing the error text
37112 side          Add an error icon to the right of the field with a popup on hover
37113 [element id]  Add the error text directly to the innerHTML of the specified element
37114 </pre>
37115      */
37116     msgTarget : 'qtip',
37117     /**
37118      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37119      */
37120     msgFx : 'normal',
37121
37122     /**
37123      * @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.
37124      */
37125     readOnly : false,
37126
37127     /**
37128      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37129      */
37130     disabled : false,
37131
37132     /**
37133      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37134      */
37135     inputType : undefined,
37136     
37137     /**
37138      * @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).
37139          */
37140         tabIndex : undefined,
37141         
37142     // private
37143     isFormField : true,
37144
37145     // private
37146     hasFocus : false,
37147     /**
37148      * @property {Roo.Element} fieldEl
37149      * Element Containing the rendered Field (with label etc.)
37150      */
37151     /**
37152      * @cfg {Mixed} value A value to initialize this field with.
37153      */
37154     value : undefined,
37155
37156     /**
37157      * @cfg {String} name The field's HTML name attribute.
37158      */
37159     /**
37160      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37161      */
37162
37163         // private ??
37164         initComponent : function(){
37165         Roo.form.Field.superclass.initComponent.call(this);
37166         this.addEvents({
37167             /**
37168              * @event focus
37169              * Fires when this field receives input focus.
37170              * @param {Roo.form.Field} this
37171              */
37172             focus : true,
37173             /**
37174              * @event blur
37175              * Fires when this field loses input focus.
37176              * @param {Roo.form.Field} this
37177              */
37178             blur : true,
37179             /**
37180              * @event specialkey
37181              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37182              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37183              * @param {Roo.form.Field} this
37184              * @param {Roo.EventObject} e The event object
37185              */
37186             specialkey : true,
37187             /**
37188              * @event change
37189              * Fires just before the field blurs if the field value has changed.
37190              * @param {Roo.form.Field} this
37191              * @param {Mixed} newValue The new value
37192              * @param {Mixed} oldValue The original value
37193              */
37194             change : true,
37195             /**
37196              * @event invalid
37197              * Fires after the field has been marked as invalid.
37198              * @param {Roo.form.Field} this
37199              * @param {String} msg The validation message
37200              */
37201             invalid : true,
37202             /**
37203              * @event valid
37204              * Fires after the field has been validated with no errors.
37205              * @param {Roo.form.Field} this
37206              */
37207             valid : true,
37208              /**
37209              * @event keyup
37210              * Fires after the key up
37211              * @param {Roo.form.Field} this
37212              * @param {Roo.EventObject}  e The event Object
37213              */
37214             keyup : true
37215         });
37216     },
37217
37218     /**
37219      * Returns the name attribute of the field if available
37220      * @return {String} name The field name
37221      */
37222     getName: function(){
37223          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37224     },
37225
37226     // private
37227     onRender : function(ct, position){
37228         Roo.form.Field.superclass.onRender.call(this, ct, position);
37229         if(!this.el){
37230             var cfg = this.getAutoCreate();
37231             if(!cfg.name){
37232                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37233             }
37234             if (!cfg.name.length) {
37235                 delete cfg.name;
37236             }
37237             if(this.inputType){
37238                 cfg.type = this.inputType;
37239             }
37240             this.el = ct.createChild(cfg, position);
37241         }
37242         var type = this.el.dom.type;
37243         if(type){
37244             if(type == 'password'){
37245                 type = 'text';
37246             }
37247             this.el.addClass('x-form-'+type);
37248         }
37249         if(this.readOnly){
37250             this.el.dom.readOnly = true;
37251         }
37252         if(this.tabIndex !== undefined){
37253             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37254         }
37255
37256         this.el.addClass([this.fieldClass, this.cls]);
37257         this.initValue();
37258     },
37259
37260     /**
37261      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37262      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37263      * @return {Roo.form.Field} this
37264      */
37265     applyTo : function(target){
37266         this.allowDomMove = false;
37267         this.el = Roo.get(target);
37268         this.render(this.el.dom.parentNode);
37269         return this;
37270     },
37271
37272     // private
37273     initValue : function(){
37274         if(this.value !== undefined){
37275             this.setValue(this.value);
37276         }else if(this.el.dom.value.length > 0){
37277             this.setValue(this.el.dom.value);
37278         }
37279     },
37280
37281     /**
37282      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37283      */
37284     isDirty : function() {
37285         if(this.disabled) {
37286             return false;
37287         }
37288         return String(this.getValue()) !== String(this.originalValue);
37289     },
37290
37291     // private
37292     afterRender : function(){
37293         Roo.form.Field.superclass.afterRender.call(this);
37294         this.initEvents();
37295     },
37296
37297     // private
37298     fireKey : function(e){
37299         //Roo.log('field ' + e.getKey());
37300         if(e.isNavKeyPress()){
37301             this.fireEvent("specialkey", this, e);
37302         }
37303     },
37304
37305     /**
37306      * Resets the current field value to the originally loaded value and clears any validation messages
37307      */
37308     reset : function(){
37309         this.setValue(this.resetValue);
37310         this.clearInvalid();
37311     },
37312
37313     // private
37314     initEvents : function(){
37315         // safari killled keypress - so keydown is now used..
37316         this.el.on("keydown" , this.fireKey,  this);
37317         this.el.on("focus", this.onFocus,  this);
37318         this.el.on("blur", this.onBlur,  this);
37319         this.el.relayEvent('keyup', this);
37320
37321         // reference to original value for reset
37322         this.originalValue = this.getValue();
37323         this.resetValue =  this.getValue();
37324     },
37325
37326     // private
37327     onFocus : function(){
37328         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37329             this.el.addClass(this.focusClass);
37330         }
37331         if(!this.hasFocus){
37332             this.hasFocus = true;
37333             this.startValue = this.getValue();
37334             this.fireEvent("focus", this);
37335         }
37336     },
37337
37338     beforeBlur : Roo.emptyFn,
37339
37340     // private
37341     onBlur : function(){
37342         this.beforeBlur();
37343         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37344             this.el.removeClass(this.focusClass);
37345         }
37346         this.hasFocus = false;
37347         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37348             this.validate();
37349         }
37350         var v = this.getValue();
37351         if(String(v) !== String(this.startValue)){
37352             this.fireEvent('change', this, v, this.startValue);
37353         }
37354         this.fireEvent("blur", this);
37355     },
37356
37357     /**
37358      * Returns whether or not the field value is currently valid
37359      * @param {Boolean} preventMark True to disable marking the field invalid
37360      * @return {Boolean} True if the value is valid, else false
37361      */
37362     isValid : function(preventMark){
37363         if(this.disabled){
37364             return true;
37365         }
37366         var restore = this.preventMark;
37367         this.preventMark = preventMark === true;
37368         var v = this.validateValue(this.processValue(this.getRawValue()));
37369         this.preventMark = restore;
37370         return v;
37371     },
37372
37373     /**
37374      * Validates the field value
37375      * @return {Boolean} True if the value is valid, else false
37376      */
37377     validate : function(){
37378         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37379             this.clearInvalid();
37380             return true;
37381         }
37382         return false;
37383     },
37384
37385     processValue : function(value){
37386         return value;
37387     },
37388
37389     // private
37390     // Subclasses should provide the validation implementation by overriding this
37391     validateValue : function(value){
37392         return true;
37393     },
37394
37395     /**
37396      * Mark this field as invalid
37397      * @param {String} msg The validation message
37398      */
37399     markInvalid : function(msg){
37400         if(!this.rendered || this.preventMark){ // not rendered
37401             return;
37402         }
37403         
37404         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37405         
37406         obj.el.addClass(this.invalidClass);
37407         msg = msg || this.invalidText;
37408         switch(this.msgTarget){
37409             case 'qtip':
37410                 obj.el.dom.qtip = msg;
37411                 obj.el.dom.qclass = 'x-form-invalid-tip';
37412                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37413                     Roo.QuickTips.enable();
37414                 }
37415                 break;
37416             case 'title':
37417                 this.el.dom.title = msg;
37418                 break;
37419             case 'under':
37420                 if(!this.errorEl){
37421                     var elp = this.el.findParent('.x-form-element', 5, true);
37422                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37423                     this.errorEl.setWidth(elp.getWidth(true)-20);
37424                 }
37425                 this.errorEl.update(msg);
37426                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37427                 break;
37428             case 'side':
37429                 if(!this.errorIcon){
37430                     var elp = this.el.findParent('.x-form-element', 5, true);
37431                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37432                 }
37433                 this.alignErrorIcon();
37434                 this.errorIcon.dom.qtip = msg;
37435                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37436                 this.errorIcon.show();
37437                 this.on('resize', this.alignErrorIcon, this);
37438                 break;
37439             default:
37440                 var t = Roo.getDom(this.msgTarget);
37441                 t.innerHTML = msg;
37442                 t.style.display = this.msgDisplay;
37443                 break;
37444         }
37445         this.fireEvent('invalid', this, msg);
37446     },
37447
37448     // private
37449     alignErrorIcon : function(){
37450         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37451     },
37452
37453     /**
37454      * Clear any invalid styles/messages for this field
37455      */
37456     clearInvalid : function(){
37457         if(!this.rendered || this.preventMark){ // not rendered
37458             return;
37459         }
37460         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37461         
37462         obj.el.removeClass(this.invalidClass);
37463         switch(this.msgTarget){
37464             case 'qtip':
37465                 obj.el.dom.qtip = '';
37466                 break;
37467             case 'title':
37468                 this.el.dom.title = '';
37469                 break;
37470             case 'under':
37471                 if(this.errorEl){
37472                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37473                 }
37474                 break;
37475             case 'side':
37476                 if(this.errorIcon){
37477                     this.errorIcon.dom.qtip = '';
37478                     this.errorIcon.hide();
37479                     this.un('resize', this.alignErrorIcon, this);
37480                 }
37481                 break;
37482             default:
37483                 var t = Roo.getDom(this.msgTarget);
37484                 t.innerHTML = '';
37485                 t.style.display = 'none';
37486                 break;
37487         }
37488         this.fireEvent('valid', this);
37489     },
37490
37491     /**
37492      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37493      * @return {Mixed} value The field value
37494      */
37495     getRawValue : function(){
37496         var v = this.el.getValue();
37497         
37498         return v;
37499     },
37500
37501     /**
37502      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37503      * @return {Mixed} value The field value
37504      */
37505     getValue : function(){
37506         var v = this.el.getValue();
37507          
37508         return v;
37509     },
37510
37511     /**
37512      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37513      * @param {Mixed} value The value to set
37514      */
37515     setRawValue : function(v){
37516         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37517     },
37518
37519     /**
37520      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37521      * @param {Mixed} value The value to set
37522      */
37523     setValue : function(v){
37524         this.value = v;
37525         if(this.rendered){
37526             this.el.dom.value = (v === null || v === undefined ? '' : v);
37527              this.validate();
37528         }
37529     },
37530
37531     adjustSize : function(w, h){
37532         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37533         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37534         return s;
37535     },
37536
37537     adjustWidth : function(tag, w){
37538         tag = tag.toLowerCase();
37539         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37540             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37541                 if(tag == 'input'){
37542                     return w + 2;
37543                 }
37544                 if(tag == 'textarea'){
37545                     return w-2;
37546                 }
37547             }else if(Roo.isOpera){
37548                 if(tag == 'input'){
37549                     return w + 2;
37550                 }
37551                 if(tag == 'textarea'){
37552                     return w-2;
37553                 }
37554             }
37555         }
37556         return w;
37557     }
37558 });
37559
37560
37561 // anything other than normal should be considered experimental
37562 Roo.form.Field.msgFx = {
37563     normal : {
37564         show: function(msgEl, f){
37565             msgEl.setDisplayed('block');
37566         },
37567
37568         hide : function(msgEl, f){
37569             msgEl.setDisplayed(false).update('');
37570         }
37571     },
37572
37573     slide : {
37574         show: function(msgEl, f){
37575             msgEl.slideIn('t', {stopFx:true});
37576         },
37577
37578         hide : function(msgEl, f){
37579             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37580         }
37581     },
37582
37583     slideRight : {
37584         show: function(msgEl, f){
37585             msgEl.fixDisplay();
37586             msgEl.alignTo(f.el, 'tl-tr');
37587             msgEl.slideIn('l', {stopFx:true});
37588         },
37589
37590         hide : function(msgEl, f){
37591             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37592         }
37593     }
37594 };/*
37595  * Based on:
37596  * Ext JS Library 1.1.1
37597  * Copyright(c) 2006-2007, Ext JS, LLC.
37598  *
37599  * Originally Released Under LGPL - original licence link has changed is not relivant.
37600  *
37601  * Fork - LGPL
37602  * <script type="text/javascript">
37603  */
37604  
37605
37606 /**
37607  * @class Roo.form.TextField
37608  * @extends Roo.form.Field
37609  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37610  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37611  * @constructor
37612  * Creates a new TextField
37613  * @param {Object} config Configuration options
37614  */
37615 Roo.form.TextField = function(config){
37616     Roo.form.TextField.superclass.constructor.call(this, config);
37617     this.addEvents({
37618         /**
37619          * @event autosize
37620          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37621          * according to the default logic, but this event provides a hook for the developer to apply additional
37622          * logic at runtime to resize the field if needed.
37623              * @param {Roo.form.Field} this This text field
37624              * @param {Number} width The new field width
37625              */
37626         autosize : true
37627     });
37628 };
37629
37630 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37631     /**
37632      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37633      */
37634     grow : false,
37635     /**
37636      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37637      */
37638     growMin : 30,
37639     /**
37640      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37641      */
37642     growMax : 800,
37643     /**
37644      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37645      */
37646     vtype : null,
37647     /**
37648      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37649      */
37650     maskRe : null,
37651     /**
37652      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37653      */
37654     disableKeyFilter : false,
37655     /**
37656      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37657      */
37658     allowBlank : true,
37659     /**
37660      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37661      */
37662     minLength : 0,
37663     /**
37664      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37665      */
37666     maxLength : Number.MAX_VALUE,
37667     /**
37668      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37669      */
37670     minLengthText : "The minimum length for this field is {0}",
37671     /**
37672      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37673      */
37674     maxLengthText : "The maximum length for this field is {0}",
37675     /**
37676      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37677      */
37678     selectOnFocus : false,
37679     /**
37680      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37681      */
37682     blankText : "This field is required",
37683     /**
37684      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37685      * If available, this function will be called only after the basic validators all return true, and will be passed the
37686      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37687      */
37688     validator : null,
37689     /**
37690      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37691      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37692      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37693      */
37694     regex : null,
37695     /**
37696      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37697      */
37698     regexText : "",
37699     /**
37700      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37701      */
37702     emptyText : null,
37703    
37704
37705     // private
37706     initEvents : function()
37707     {
37708         if (this.emptyText) {
37709             this.el.attr('placeholder', this.emptyText);
37710         }
37711         
37712         Roo.form.TextField.superclass.initEvents.call(this);
37713         if(this.validationEvent == 'keyup'){
37714             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37715             this.el.on('keyup', this.filterValidation, this);
37716         }
37717         else if(this.validationEvent !== false){
37718             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37719         }
37720         
37721         if(this.selectOnFocus){
37722             this.on("focus", this.preFocus, this);
37723             
37724         }
37725         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37726             this.el.on("keypress", this.filterKeys, this);
37727         }
37728         if(this.grow){
37729             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37730             this.el.on("click", this.autoSize,  this);
37731         }
37732         if(this.el.is('input[type=password]') && Roo.isSafari){
37733             this.el.on('keydown', this.SafariOnKeyDown, this);
37734         }
37735     },
37736
37737     processValue : function(value){
37738         if(this.stripCharsRe){
37739             var newValue = value.replace(this.stripCharsRe, '');
37740             if(newValue !== value){
37741                 this.setRawValue(newValue);
37742                 return newValue;
37743             }
37744         }
37745         return value;
37746     },
37747
37748     filterValidation : function(e){
37749         if(!e.isNavKeyPress()){
37750             this.validationTask.delay(this.validationDelay);
37751         }
37752     },
37753
37754     // private
37755     onKeyUp : function(e){
37756         if(!e.isNavKeyPress()){
37757             this.autoSize();
37758         }
37759     },
37760
37761     /**
37762      * Resets the current field value to the originally-loaded value and clears any validation messages.
37763      *  
37764      */
37765     reset : function(){
37766         Roo.form.TextField.superclass.reset.call(this);
37767        
37768     },
37769
37770     
37771     // private
37772     preFocus : function(){
37773         
37774         if(this.selectOnFocus){
37775             this.el.dom.select();
37776         }
37777     },
37778
37779     
37780     // private
37781     filterKeys : function(e){
37782         var k = e.getKey();
37783         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37784             return;
37785         }
37786         var c = e.getCharCode(), cc = String.fromCharCode(c);
37787         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37788             return;
37789         }
37790         if(!this.maskRe.test(cc)){
37791             e.stopEvent();
37792         }
37793     },
37794
37795     setValue : function(v){
37796         
37797         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37798         
37799         this.autoSize();
37800     },
37801
37802     /**
37803      * Validates a value according to the field's validation rules and marks the field as invalid
37804      * if the validation fails
37805      * @param {Mixed} value The value to validate
37806      * @return {Boolean} True if the value is valid, else false
37807      */
37808     validateValue : function(value){
37809         if(value.length < 1)  { // if it's blank
37810              if(this.allowBlank){
37811                 this.clearInvalid();
37812                 return true;
37813              }else{
37814                 this.markInvalid(this.blankText);
37815                 return false;
37816              }
37817         }
37818         if(value.length < this.minLength){
37819             this.markInvalid(String.format(this.minLengthText, this.minLength));
37820             return false;
37821         }
37822         if(value.length > this.maxLength){
37823             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37824             return false;
37825         }
37826         if(this.vtype){
37827             var vt = Roo.form.VTypes;
37828             if(!vt[this.vtype](value, this)){
37829                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37830                 return false;
37831             }
37832         }
37833         if(typeof this.validator == "function"){
37834             var msg = this.validator(value);
37835             if(msg !== true){
37836                 this.markInvalid(msg);
37837                 return false;
37838             }
37839         }
37840         if(this.regex && !this.regex.test(value)){
37841             this.markInvalid(this.regexText);
37842             return false;
37843         }
37844         return true;
37845     },
37846
37847     /**
37848      * Selects text in this field
37849      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37850      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37851      */
37852     selectText : function(start, end){
37853         var v = this.getRawValue();
37854         if(v.length > 0){
37855             start = start === undefined ? 0 : start;
37856             end = end === undefined ? v.length : end;
37857             var d = this.el.dom;
37858             if(d.setSelectionRange){
37859                 d.setSelectionRange(start, end);
37860             }else if(d.createTextRange){
37861                 var range = d.createTextRange();
37862                 range.moveStart("character", start);
37863                 range.moveEnd("character", v.length-end);
37864                 range.select();
37865             }
37866         }
37867     },
37868
37869     /**
37870      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37871      * This only takes effect if grow = true, and fires the autosize event.
37872      */
37873     autoSize : function(){
37874         if(!this.grow || !this.rendered){
37875             return;
37876         }
37877         if(!this.metrics){
37878             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37879         }
37880         var el = this.el;
37881         var v = el.dom.value;
37882         var d = document.createElement('div');
37883         d.appendChild(document.createTextNode(v));
37884         v = d.innerHTML;
37885         d = null;
37886         v += "&#160;";
37887         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37888         this.el.setWidth(w);
37889         this.fireEvent("autosize", this, w);
37890     },
37891     
37892     // private
37893     SafariOnKeyDown : function(event)
37894     {
37895         // this is a workaround for a password hang bug on chrome/ webkit.
37896         
37897         var isSelectAll = false;
37898         
37899         if(this.el.dom.selectionEnd > 0){
37900             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37901         }
37902         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37903             event.preventDefault();
37904             this.setValue('');
37905             return;
37906         }
37907         
37908         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37909             
37910             event.preventDefault();
37911             // this is very hacky as keydown always get's upper case.
37912             
37913             var cc = String.fromCharCode(event.getCharCode());
37914             
37915             
37916             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37917             
37918         }
37919         
37920         
37921     }
37922 });/*
37923  * Based on:
37924  * Ext JS Library 1.1.1
37925  * Copyright(c) 2006-2007, Ext JS, LLC.
37926  *
37927  * Originally Released Under LGPL - original licence link has changed is not relivant.
37928  *
37929  * Fork - LGPL
37930  * <script type="text/javascript">
37931  */
37932  
37933 /**
37934  * @class Roo.form.Hidden
37935  * @extends Roo.form.TextField
37936  * Simple Hidden element used on forms 
37937  * 
37938  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37939  * 
37940  * @constructor
37941  * Creates a new Hidden form element.
37942  * @param {Object} config Configuration options
37943  */
37944
37945
37946
37947 // easy hidden field...
37948 Roo.form.Hidden = function(config){
37949     Roo.form.Hidden.superclass.constructor.call(this, config);
37950 };
37951   
37952 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37953     fieldLabel:      '',
37954     inputType:      'hidden',
37955     width:          50,
37956     allowBlank:     true,
37957     labelSeparator: '',
37958     hidden:         true,
37959     itemCls :       'x-form-item-display-none'
37960
37961
37962 });
37963
37964
37965 /*
37966  * Based on:
37967  * Ext JS Library 1.1.1
37968  * Copyright(c) 2006-2007, Ext JS, LLC.
37969  *
37970  * Originally Released Under LGPL - original licence link has changed is not relivant.
37971  *
37972  * Fork - LGPL
37973  * <script type="text/javascript">
37974  */
37975  
37976 /**
37977  * @class Roo.form.TriggerField
37978  * @extends Roo.form.TextField
37979  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37980  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37981  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37982  * for which you can provide a custom implementation.  For example:
37983  * <pre><code>
37984 var trigger = new Roo.form.TriggerField();
37985 trigger.onTriggerClick = myTriggerFn;
37986 trigger.applyTo('my-field');
37987 </code></pre>
37988  *
37989  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37990  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37991  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37992  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37993  * @constructor
37994  * Create a new TriggerField.
37995  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37996  * to the base TextField)
37997  */
37998 Roo.form.TriggerField = function(config){
37999     this.mimicing = false;
38000     Roo.form.TriggerField.superclass.constructor.call(this, config);
38001 };
38002
38003 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
38004     /**
38005      * @cfg {String} triggerClass A CSS class to apply to the trigger
38006      */
38007     /**
38008      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38009      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38010      */
38011     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38012     /**
38013      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38014      */
38015     hideTrigger:false,
38016
38017     /** @cfg {Boolean} grow @hide */
38018     /** @cfg {Number} growMin @hide */
38019     /** @cfg {Number} growMax @hide */
38020
38021     /**
38022      * @hide 
38023      * @method
38024      */
38025     autoSize: Roo.emptyFn,
38026     // private
38027     monitorTab : true,
38028     // private
38029     deferHeight : true,
38030
38031     
38032     actionMode : 'wrap',
38033     // private
38034     onResize : function(w, h){
38035         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38036         if(typeof w == 'number'){
38037             var x = w - this.trigger.getWidth();
38038             this.el.setWidth(this.adjustWidth('input', x));
38039             this.trigger.setStyle('left', x+'px');
38040         }
38041     },
38042
38043     // private
38044     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38045
38046     // private
38047     getResizeEl : function(){
38048         return this.wrap;
38049     },
38050
38051     // private
38052     getPositionEl : function(){
38053         return this.wrap;
38054     },
38055
38056     // private
38057     alignErrorIcon : function(){
38058         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38059     },
38060
38061     // private
38062     onRender : function(ct, position){
38063         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38064         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38065         this.trigger = this.wrap.createChild(this.triggerConfig ||
38066                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38067         if(this.hideTrigger){
38068             this.trigger.setDisplayed(false);
38069         }
38070         this.initTrigger();
38071         if(!this.width){
38072             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38073         }
38074     },
38075
38076     // private
38077     initTrigger : function(){
38078         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38079         this.trigger.addClassOnOver('x-form-trigger-over');
38080         this.trigger.addClassOnClick('x-form-trigger-click');
38081     },
38082
38083     // private
38084     onDestroy : function(){
38085         if(this.trigger){
38086             this.trigger.removeAllListeners();
38087             this.trigger.remove();
38088         }
38089         if(this.wrap){
38090             this.wrap.remove();
38091         }
38092         Roo.form.TriggerField.superclass.onDestroy.call(this);
38093     },
38094
38095     // private
38096     onFocus : function(){
38097         Roo.form.TriggerField.superclass.onFocus.call(this);
38098         if(!this.mimicing){
38099             this.wrap.addClass('x-trigger-wrap-focus');
38100             this.mimicing = true;
38101             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38102             if(this.monitorTab){
38103                 this.el.on("keydown", this.checkTab, this);
38104             }
38105         }
38106     },
38107
38108     // private
38109     checkTab : function(e){
38110         if(e.getKey() == e.TAB){
38111             this.triggerBlur();
38112         }
38113     },
38114
38115     // private
38116     onBlur : function(){
38117         // do nothing
38118     },
38119
38120     // private
38121     mimicBlur : function(e, t){
38122         if(!this.wrap.contains(t) && this.validateBlur()){
38123             this.triggerBlur();
38124         }
38125     },
38126
38127     // private
38128     triggerBlur : function(){
38129         this.mimicing = false;
38130         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38131         if(this.monitorTab){
38132             this.el.un("keydown", this.checkTab, this);
38133         }
38134         this.wrap.removeClass('x-trigger-wrap-focus');
38135         Roo.form.TriggerField.superclass.onBlur.call(this);
38136     },
38137
38138     // private
38139     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38140     validateBlur : function(e, t){
38141         return true;
38142     },
38143
38144     // private
38145     onDisable : function(){
38146         Roo.form.TriggerField.superclass.onDisable.call(this);
38147         if(this.wrap){
38148             this.wrap.addClass('x-item-disabled');
38149         }
38150     },
38151
38152     // private
38153     onEnable : function(){
38154         Roo.form.TriggerField.superclass.onEnable.call(this);
38155         if(this.wrap){
38156             this.wrap.removeClass('x-item-disabled');
38157         }
38158     },
38159
38160     // private
38161     onShow : function(){
38162         var ae = this.getActionEl();
38163         
38164         if(ae){
38165             ae.dom.style.display = '';
38166             ae.dom.style.visibility = 'visible';
38167         }
38168     },
38169
38170     // private
38171     
38172     onHide : function(){
38173         var ae = this.getActionEl();
38174         ae.dom.style.display = 'none';
38175     },
38176
38177     /**
38178      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38179      * by an implementing function.
38180      * @method
38181      * @param {EventObject} e
38182      */
38183     onTriggerClick : Roo.emptyFn
38184 });
38185
38186 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38187 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38188 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38189 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38190     initComponent : function(){
38191         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38192
38193         this.triggerConfig = {
38194             tag:'span', cls:'x-form-twin-triggers', cn:[
38195             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38196             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38197         ]};
38198     },
38199
38200     getTrigger : function(index){
38201         return this.triggers[index];
38202     },
38203
38204     initTrigger : function(){
38205         var ts = this.trigger.select('.x-form-trigger', true);
38206         this.wrap.setStyle('overflow', 'hidden');
38207         var triggerField = this;
38208         ts.each(function(t, all, index){
38209             t.hide = function(){
38210                 var w = triggerField.wrap.getWidth();
38211                 this.dom.style.display = 'none';
38212                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38213             };
38214             t.show = function(){
38215                 var w = triggerField.wrap.getWidth();
38216                 this.dom.style.display = '';
38217                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38218             };
38219             var triggerIndex = 'Trigger'+(index+1);
38220
38221             if(this['hide'+triggerIndex]){
38222                 t.dom.style.display = 'none';
38223             }
38224             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38225             t.addClassOnOver('x-form-trigger-over');
38226             t.addClassOnClick('x-form-trigger-click');
38227         }, this);
38228         this.triggers = ts.elements;
38229     },
38230
38231     onTrigger1Click : Roo.emptyFn,
38232     onTrigger2Click : Roo.emptyFn
38233 });/*
38234  * Based on:
38235  * Ext JS Library 1.1.1
38236  * Copyright(c) 2006-2007, Ext JS, LLC.
38237  *
38238  * Originally Released Under LGPL - original licence link has changed is not relivant.
38239  *
38240  * Fork - LGPL
38241  * <script type="text/javascript">
38242  */
38243  
38244 /**
38245  * @class Roo.form.TextArea
38246  * @extends Roo.form.TextField
38247  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38248  * support for auto-sizing.
38249  * @constructor
38250  * Creates a new TextArea
38251  * @param {Object} config Configuration options
38252  */
38253 Roo.form.TextArea = function(config){
38254     Roo.form.TextArea.superclass.constructor.call(this, config);
38255     // these are provided exchanges for backwards compat
38256     // minHeight/maxHeight were replaced by growMin/growMax to be
38257     // compatible with TextField growing config values
38258     if(this.minHeight !== undefined){
38259         this.growMin = this.minHeight;
38260     }
38261     if(this.maxHeight !== undefined){
38262         this.growMax = this.maxHeight;
38263     }
38264 };
38265
38266 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38267     /**
38268      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38269      */
38270     growMin : 60,
38271     /**
38272      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38273      */
38274     growMax: 1000,
38275     /**
38276      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38277      * in the field (equivalent to setting overflow: hidden, defaults to false)
38278      */
38279     preventScrollbars: false,
38280     /**
38281      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38282      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38283      */
38284
38285     // private
38286     onRender : function(ct, position){
38287         if(!this.el){
38288             this.defaultAutoCreate = {
38289                 tag: "textarea",
38290                 style:"width:300px;height:60px;",
38291                 autocomplete: "new-password"
38292             };
38293         }
38294         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38295         if(this.grow){
38296             this.textSizeEl = Roo.DomHelper.append(document.body, {
38297                 tag: "pre", cls: "x-form-grow-sizer"
38298             });
38299             if(this.preventScrollbars){
38300                 this.el.setStyle("overflow", "hidden");
38301             }
38302             this.el.setHeight(this.growMin);
38303         }
38304     },
38305
38306     onDestroy : function(){
38307         if(this.textSizeEl){
38308             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38309         }
38310         Roo.form.TextArea.superclass.onDestroy.call(this);
38311     },
38312
38313     // private
38314     onKeyUp : function(e){
38315         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38316             this.autoSize();
38317         }
38318     },
38319
38320     /**
38321      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38322      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38323      */
38324     autoSize : function(){
38325         if(!this.grow || !this.textSizeEl){
38326             return;
38327         }
38328         var el = this.el;
38329         var v = el.dom.value;
38330         var ts = this.textSizeEl;
38331
38332         ts.innerHTML = '';
38333         ts.appendChild(document.createTextNode(v));
38334         v = ts.innerHTML;
38335
38336         Roo.fly(ts).setWidth(this.el.getWidth());
38337         if(v.length < 1){
38338             v = "&#160;&#160;";
38339         }else{
38340             if(Roo.isIE){
38341                 v = v.replace(/\n/g, '<p>&#160;</p>');
38342             }
38343             v += "&#160;\n&#160;";
38344         }
38345         ts.innerHTML = v;
38346         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38347         if(h != this.lastHeight){
38348             this.lastHeight = h;
38349             this.el.setHeight(h);
38350             this.fireEvent("autosize", this, h);
38351         }
38352     }
38353 });/*
38354  * Based on:
38355  * Ext JS Library 1.1.1
38356  * Copyright(c) 2006-2007, Ext JS, LLC.
38357  *
38358  * Originally Released Under LGPL - original licence link has changed is not relivant.
38359  *
38360  * Fork - LGPL
38361  * <script type="text/javascript">
38362  */
38363  
38364
38365 /**
38366  * @class Roo.form.NumberField
38367  * @extends Roo.form.TextField
38368  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38369  * @constructor
38370  * Creates a new NumberField
38371  * @param {Object} config Configuration options
38372  */
38373 Roo.form.NumberField = function(config){
38374     Roo.form.NumberField.superclass.constructor.call(this, config);
38375 };
38376
38377 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38378     /**
38379      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38380      */
38381     fieldClass: "x-form-field x-form-num-field",
38382     /**
38383      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38384      */
38385     allowDecimals : true,
38386     /**
38387      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38388      */
38389     decimalSeparator : ".",
38390     /**
38391      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38392      */
38393     decimalPrecision : 2,
38394     /**
38395      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38396      */
38397     allowNegative : true,
38398     /**
38399      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38400      */
38401     minValue : Number.NEGATIVE_INFINITY,
38402     /**
38403      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38404      */
38405     maxValue : Number.MAX_VALUE,
38406     /**
38407      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38408      */
38409     minText : "The minimum value for this field is {0}",
38410     /**
38411      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38412      */
38413     maxText : "The maximum value for this field is {0}",
38414     /**
38415      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38416      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38417      */
38418     nanText : "{0} is not a valid number",
38419
38420     // private
38421     initEvents : function(){
38422         Roo.form.NumberField.superclass.initEvents.call(this);
38423         var allowed = "0123456789";
38424         if(this.allowDecimals){
38425             allowed += this.decimalSeparator;
38426         }
38427         if(this.allowNegative){
38428             allowed += "-";
38429         }
38430         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38431         var keyPress = function(e){
38432             var k = e.getKey();
38433             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38434                 return;
38435             }
38436             var c = e.getCharCode();
38437             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38438                 e.stopEvent();
38439             }
38440         };
38441         this.el.on("keypress", keyPress, this);
38442     },
38443
38444     // private
38445     validateValue : function(value){
38446         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38447             return false;
38448         }
38449         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38450              return true;
38451         }
38452         var num = this.parseValue(value);
38453         if(isNaN(num)){
38454             this.markInvalid(String.format(this.nanText, value));
38455             return false;
38456         }
38457         if(num < this.minValue){
38458             this.markInvalid(String.format(this.minText, this.minValue));
38459             return false;
38460         }
38461         if(num > this.maxValue){
38462             this.markInvalid(String.format(this.maxText, this.maxValue));
38463             return false;
38464         }
38465         return true;
38466     },
38467
38468     getValue : function(){
38469         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38470     },
38471
38472     // private
38473     parseValue : function(value){
38474         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38475         return isNaN(value) ? '' : value;
38476     },
38477
38478     // private
38479     fixPrecision : function(value){
38480         var nan = isNaN(value);
38481         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38482             return nan ? '' : value;
38483         }
38484         return parseFloat(value).toFixed(this.decimalPrecision);
38485     },
38486
38487     setValue : function(v){
38488         v = this.fixPrecision(v);
38489         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38490     },
38491
38492     // private
38493     decimalPrecisionFcn : function(v){
38494         return Math.floor(v);
38495     },
38496
38497     beforeBlur : function(){
38498         var v = this.parseValue(this.getRawValue());
38499         if(v){
38500             this.setValue(v);
38501         }
38502     }
38503 });/*
38504  * Based on:
38505  * Ext JS Library 1.1.1
38506  * Copyright(c) 2006-2007, Ext JS, LLC.
38507  *
38508  * Originally Released Under LGPL - original licence link has changed is not relivant.
38509  *
38510  * Fork - LGPL
38511  * <script type="text/javascript">
38512  */
38513  
38514 /**
38515  * @class Roo.form.DateField
38516  * @extends Roo.form.TriggerField
38517  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38518 * @constructor
38519 * Create a new DateField
38520 * @param {Object} config
38521  */
38522 Roo.form.DateField = function(config){
38523     Roo.form.DateField.superclass.constructor.call(this, config);
38524     
38525       this.addEvents({
38526          
38527         /**
38528          * @event select
38529          * Fires when a date is selected
38530              * @param {Roo.form.DateField} combo This combo box
38531              * @param {Date} date The date selected
38532              */
38533         'select' : true
38534          
38535     });
38536     
38537     
38538     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38539     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38540     this.ddMatch = null;
38541     if(this.disabledDates){
38542         var dd = this.disabledDates;
38543         var re = "(?:";
38544         for(var i = 0; i < dd.length; i++){
38545             re += dd[i];
38546             if(i != dd.length-1) re += "|";
38547         }
38548         this.ddMatch = new RegExp(re + ")");
38549     }
38550 };
38551
38552 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38553     /**
38554      * @cfg {String} format
38555      * The default date format string which can be overriden for localization support.  The format must be
38556      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38557      */
38558     format : "m/d/y",
38559     /**
38560      * @cfg {String} altFormats
38561      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38562      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38563      */
38564     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38565     /**
38566      * @cfg {Array} disabledDays
38567      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38568      */
38569     disabledDays : null,
38570     /**
38571      * @cfg {String} disabledDaysText
38572      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38573      */
38574     disabledDaysText : "Disabled",
38575     /**
38576      * @cfg {Array} disabledDates
38577      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38578      * expression so they are very powerful. Some examples:
38579      * <ul>
38580      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38581      * <li>["03/08", "09/16"] would disable those days for every year</li>
38582      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38583      * <li>["03/../2006"] would disable every day in March 2006</li>
38584      * <li>["^03"] would disable every day in every March</li>
38585      * </ul>
38586      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38587      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38588      */
38589     disabledDates : null,
38590     /**
38591      * @cfg {String} disabledDatesText
38592      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38593      */
38594     disabledDatesText : "Disabled",
38595     /**
38596      * @cfg {Date/String} minValue
38597      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38598      * valid format (defaults to null).
38599      */
38600     minValue : null,
38601     /**
38602      * @cfg {Date/String} maxValue
38603      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38604      * valid format (defaults to null).
38605      */
38606     maxValue : null,
38607     /**
38608      * @cfg {String} minText
38609      * The error text to display when the date in the cell is before minValue (defaults to
38610      * 'The date in this field must be after {minValue}').
38611      */
38612     minText : "The date in this field must be equal to or after {0}",
38613     /**
38614      * @cfg {String} maxText
38615      * The error text to display when the date in the cell is after maxValue (defaults to
38616      * 'The date in this field must be before {maxValue}').
38617      */
38618     maxText : "The date in this field must be equal to or before {0}",
38619     /**
38620      * @cfg {String} invalidText
38621      * The error text to display when the date in the field is invalid (defaults to
38622      * '{value} is not a valid date - it must be in the format {format}').
38623      */
38624     invalidText : "{0} is not a valid date - it must be in the format {1}",
38625     /**
38626      * @cfg {String} triggerClass
38627      * An additional CSS class used to style the trigger button.  The trigger will always get the
38628      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38629      * which displays a calendar icon).
38630      */
38631     triggerClass : 'x-form-date-trigger',
38632     
38633
38634     /**
38635      * @cfg {Boolean} useIso
38636      * if enabled, then the date field will use a hidden field to store the 
38637      * real value as iso formated date. default (false)
38638      */ 
38639     useIso : false,
38640     /**
38641      * @cfg {String/Object} autoCreate
38642      * A DomHelper element spec, or true for a default element spec (defaults to
38643      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38644      */ 
38645     // private
38646     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38647     
38648     // private
38649     hiddenField: false,
38650     
38651     onRender : function(ct, position)
38652     {
38653         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38654         if (this.useIso) {
38655             //this.el.dom.removeAttribute('name'); 
38656             Roo.log("Changing name?");
38657             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38658             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38659                     'before', true);
38660             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38661             // prevent input submission
38662             this.hiddenName = this.name;
38663         }
38664             
38665             
38666     },
38667     
38668     // private
38669     validateValue : function(value)
38670     {
38671         value = this.formatDate(value);
38672         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38673             Roo.log('super failed');
38674             return false;
38675         }
38676         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38677              return true;
38678         }
38679         var svalue = value;
38680         value = this.parseDate(value);
38681         if(!value){
38682             Roo.log('parse date failed' + svalue);
38683             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38684             return false;
38685         }
38686         var time = value.getTime();
38687         if(this.minValue && time < this.minValue.getTime()){
38688             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38689             return false;
38690         }
38691         if(this.maxValue && time > this.maxValue.getTime()){
38692             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38693             return false;
38694         }
38695         if(this.disabledDays){
38696             var day = value.getDay();
38697             for(var i = 0; i < this.disabledDays.length; i++) {
38698                 if(day === this.disabledDays[i]){
38699                     this.markInvalid(this.disabledDaysText);
38700                     return false;
38701                 }
38702             }
38703         }
38704         var fvalue = this.formatDate(value);
38705         if(this.ddMatch && this.ddMatch.test(fvalue)){
38706             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38707             return false;
38708         }
38709         return true;
38710     },
38711
38712     // private
38713     // Provides logic to override the default TriggerField.validateBlur which just returns true
38714     validateBlur : function(){
38715         return !this.menu || !this.menu.isVisible();
38716     },
38717     
38718     getName: function()
38719     {
38720         // returns hidden if it's set..
38721         if (!this.rendered) {return ''};
38722         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38723         
38724     },
38725
38726     /**
38727      * Returns the current date value of the date field.
38728      * @return {Date} The date value
38729      */
38730     getValue : function(){
38731         
38732         return  this.hiddenField ?
38733                 this.hiddenField.value :
38734                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38735     },
38736
38737     /**
38738      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38739      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38740      * (the default format used is "m/d/y").
38741      * <br />Usage:
38742      * <pre><code>
38743 //All of these calls set the same date value (May 4, 2006)
38744
38745 //Pass a date object:
38746 var dt = new Date('5/4/06');
38747 dateField.setValue(dt);
38748
38749 //Pass a date string (default format):
38750 dateField.setValue('5/4/06');
38751
38752 //Pass a date string (custom format):
38753 dateField.format = 'Y-m-d';
38754 dateField.setValue('2006-5-4');
38755 </code></pre>
38756      * @param {String/Date} date The date or valid date string
38757      */
38758     setValue : function(date){
38759         if (this.hiddenField) {
38760             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38761         }
38762         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38763         // make sure the value field is always stored as a date..
38764         this.value = this.parseDate(date);
38765         
38766         
38767     },
38768
38769     // private
38770     parseDate : function(value){
38771         if(!value || value instanceof Date){
38772             return value;
38773         }
38774         var v = Date.parseDate(value, this.format);
38775          if (!v && this.useIso) {
38776             v = Date.parseDate(value, 'Y-m-d');
38777         }
38778         if(!v && this.altFormats){
38779             if(!this.altFormatsArray){
38780                 this.altFormatsArray = this.altFormats.split("|");
38781             }
38782             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38783                 v = Date.parseDate(value, this.altFormatsArray[i]);
38784             }
38785         }
38786         return v;
38787     },
38788
38789     // private
38790     formatDate : function(date, fmt){
38791         return (!date || !(date instanceof Date)) ?
38792                date : date.dateFormat(fmt || this.format);
38793     },
38794
38795     // private
38796     menuListeners : {
38797         select: function(m, d){
38798             
38799             this.setValue(d);
38800             this.fireEvent('select', this, d);
38801         },
38802         show : function(){ // retain focus styling
38803             this.onFocus();
38804         },
38805         hide : function(){
38806             this.focus.defer(10, this);
38807             var ml = this.menuListeners;
38808             this.menu.un("select", ml.select,  this);
38809             this.menu.un("show", ml.show,  this);
38810             this.menu.un("hide", ml.hide,  this);
38811         }
38812     },
38813
38814     // private
38815     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38816     onTriggerClick : function(){
38817         if(this.disabled){
38818             return;
38819         }
38820         if(this.menu == null){
38821             this.menu = new Roo.menu.DateMenu();
38822         }
38823         Roo.apply(this.menu.picker,  {
38824             showClear: this.allowBlank,
38825             minDate : this.minValue,
38826             maxDate : this.maxValue,
38827             disabledDatesRE : this.ddMatch,
38828             disabledDatesText : this.disabledDatesText,
38829             disabledDays : this.disabledDays,
38830             disabledDaysText : this.disabledDaysText,
38831             format : this.useIso ? 'Y-m-d' : this.format,
38832             minText : String.format(this.minText, this.formatDate(this.minValue)),
38833             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38834         });
38835         this.menu.on(Roo.apply({}, this.menuListeners, {
38836             scope:this
38837         }));
38838         this.menu.picker.setValue(this.getValue() || new Date());
38839         this.menu.show(this.el, "tl-bl?");
38840     },
38841
38842     beforeBlur : function(){
38843         var v = this.parseDate(this.getRawValue());
38844         if(v){
38845             this.setValue(v);
38846         }
38847     },
38848
38849     /*@
38850      * overide
38851      * 
38852      */
38853     isDirty : function() {
38854         if(this.disabled) {
38855             return false;
38856         }
38857         
38858         if(typeof(this.startValue) === 'undefined'){
38859             return false;
38860         }
38861         
38862         return String(this.getValue()) !== String(this.startValue);
38863         
38864     }
38865 });/*
38866  * Based on:
38867  * Ext JS Library 1.1.1
38868  * Copyright(c) 2006-2007, Ext JS, LLC.
38869  *
38870  * Originally Released Under LGPL - original licence link has changed is not relivant.
38871  *
38872  * Fork - LGPL
38873  * <script type="text/javascript">
38874  */
38875  
38876 /**
38877  * @class Roo.form.MonthField
38878  * @extends Roo.form.TriggerField
38879  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38880 * @constructor
38881 * Create a new MonthField
38882 * @param {Object} config
38883  */
38884 Roo.form.MonthField = function(config){
38885     
38886     Roo.form.MonthField.superclass.constructor.call(this, config);
38887     
38888       this.addEvents({
38889          
38890         /**
38891          * @event select
38892          * Fires when a date is selected
38893              * @param {Roo.form.MonthFieeld} combo This combo box
38894              * @param {Date} date The date selected
38895              */
38896         'select' : true
38897          
38898     });
38899     
38900     
38901     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38902     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38903     this.ddMatch = null;
38904     if(this.disabledDates){
38905         var dd = this.disabledDates;
38906         var re = "(?:";
38907         for(var i = 0; i < dd.length; i++){
38908             re += dd[i];
38909             if(i != dd.length-1) re += "|";
38910         }
38911         this.ddMatch = new RegExp(re + ")");
38912     }
38913 };
38914
38915 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38916     /**
38917      * @cfg {String} format
38918      * The default date format string which can be overriden for localization support.  The format must be
38919      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38920      */
38921     format : "M Y",
38922     /**
38923      * @cfg {String} altFormats
38924      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38925      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38926      */
38927     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38928     /**
38929      * @cfg {Array} disabledDays
38930      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38931      */
38932     disabledDays : [0,1,2,3,4,5,6],
38933     /**
38934      * @cfg {String} disabledDaysText
38935      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38936      */
38937     disabledDaysText : "Disabled",
38938     /**
38939      * @cfg {Array} disabledDates
38940      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38941      * expression so they are very powerful. Some examples:
38942      * <ul>
38943      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38944      * <li>["03/08", "09/16"] would disable those days for every year</li>
38945      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38946      * <li>["03/../2006"] would disable every day in March 2006</li>
38947      * <li>["^03"] would disable every day in every March</li>
38948      * </ul>
38949      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38950      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38951      */
38952     disabledDates : null,
38953     /**
38954      * @cfg {String} disabledDatesText
38955      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38956      */
38957     disabledDatesText : "Disabled",
38958     /**
38959      * @cfg {Date/String} minValue
38960      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38961      * valid format (defaults to null).
38962      */
38963     minValue : null,
38964     /**
38965      * @cfg {Date/String} maxValue
38966      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38967      * valid format (defaults to null).
38968      */
38969     maxValue : null,
38970     /**
38971      * @cfg {String} minText
38972      * The error text to display when the date in the cell is before minValue (defaults to
38973      * 'The date in this field must be after {minValue}').
38974      */
38975     minText : "The date in this field must be equal to or after {0}",
38976     /**
38977      * @cfg {String} maxTextf
38978      * The error text to display when the date in the cell is after maxValue (defaults to
38979      * 'The date in this field must be before {maxValue}').
38980      */
38981     maxText : "The date in this field must be equal to or before {0}",
38982     /**
38983      * @cfg {String} invalidText
38984      * The error text to display when the date in the field is invalid (defaults to
38985      * '{value} is not a valid date - it must be in the format {format}').
38986      */
38987     invalidText : "{0} is not a valid date - it must be in the format {1}",
38988     /**
38989      * @cfg {String} triggerClass
38990      * An additional CSS class used to style the trigger button.  The trigger will always get the
38991      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38992      * which displays a calendar icon).
38993      */
38994     triggerClass : 'x-form-date-trigger',
38995     
38996
38997     /**
38998      * @cfg {Boolean} useIso
38999      * if enabled, then the date field will use a hidden field to store the 
39000      * real value as iso formated date. default (true)
39001      */ 
39002     useIso : true,
39003     /**
39004      * @cfg {String/Object} autoCreate
39005      * A DomHelper element spec, or true for a default element spec (defaults to
39006      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39007      */ 
39008     // private
39009     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39010     
39011     // private
39012     hiddenField: false,
39013     
39014     hideMonthPicker : false,
39015     
39016     onRender : function(ct, position)
39017     {
39018         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39019         if (this.useIso) {
39020             this.el.dom.removeAttribute('name'); 
39021             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39022                     'before', true);
39023             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39024             // prevent input submission
39025             this.hiddenName = this.name;
39026         }
39027             
39028             
39029     },
39030     
39031     // private
39032     validateValue : function(value)
39033     {
39034         value = this.formatDate(value);
39035         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39036             return false;
39037         }
39038         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39039              return true;
39040         }
39041         var svalue = value;
39042         value = this.parseDate(value);
39043         if(!value){
39044             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39045             return false;
39046         }
39047         var time = value.getTime();
39048         if(this.minValue && time < this.minValue.getTime()){
39049             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39050             return false;
39051         }
39052         if(this.maxValue && time > this.maxValue.getTime()){
39053             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39054             return false;
39055         }
39056         /*if(this.disabledDays){
39057             var day = value.getDay();
39058             for(var i = 0; i < this.disabledDays.length; i++) {
39059                 if(day === this.disabledDays[i]){
39060                     this.markInvalid(this.disabledDaysText);
39061                     return false;
39062                 }
39063             }
39064         }
39065         */
39066         var fvalue = this.formatDate(value);
39067         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39068             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39069             return false;
39070         }
39071         */
39072         return true;
39073     },
39074
39075     // private
39076     // Provides logic to override the default TriggerField.validateBlur which just returns true
39077     validateBlur : function(){
39078         return !this.menu || !this.menu.isVisible();
39079     },
39080
39081     /**
39082      * Returns the current date value of the date field.
39083      * @return {Date} The date value
39084      */
39085     getValue : function(){
39086         
39087         
39088         
39089         return  this.hiddenField ?
39090                 this.hiddenField.value :
39091                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39092     },
39093
39094     /**
39095      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39096      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39097      * (the default format used is "m/d/y").
39098      * <br />Usage:
39099      * <pre><code>
39100 //All of these calls set the same date value (May 4, 2006)
39101
39102 //Pass a date object:
39103 var dt = new Date('5/4/06');
39104 monthField.setValue(dt);
39105
39106 //Pass a date string (default format):
39107 monthField.setValue('5/4/06');
39108
39109 //Pass a date string (custom format):
39110 monthField.format = 'Y-m-d';
39111 monthField.setValue('2006-5-4');
39112 </code></pre>
39113      * @param {String/Date} date The date or valid date string
39114      */
39115     setValue : function(date){
39116         Roo.log('month setValue' + date);
39117         // can only be first of month..
39118         
39119         var val = this.parseDate(date);
39120         
39121         if (this.hiddenField) {
39122             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39123         }
39124         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39125         this.value = this.parseDate(date);
39126     },
39127
39128     // private
39129     parseDate : function(value){
39130         if(!value || value instanceof Date){
39131             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39132             return value;
39133         }
39134         var v = Date.parseDate(value, this.format);
39135         if (!v && this.useIso) {
39136             v = Date.parseDate(value, 'Y-m-d');
39137         }
39138         if (v) {
39139             // 
39140             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39141         }
39142         
39143         
39144         if(!v && this.altFormats){
39145             if(!this.altFormatsArray){
39146                 this.altFormatsArray = this.altFormats.split("|");
39147             }
39148             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39149                 v = Date.parseDate(value, this.altFormatsArray[i]);
39150             }
39151         }
39152         return v;
39153     },
39154
39155     // private
39156     formatDate : function(date, fmt){
39157         return (!date || !(date instanceof Date)) ?
39158                date : date.dateFormat(fmt || this.format);
39159     },
39160
39161     // private
39162     menuListeners : {
39163         select: function(m, d){
39164             this.setValue(d);
39165             this.fireEvent('select', this, d);
39166         },
39167         show : function(){ // retain focus styling
39168             this.onFocus();
39169         },
39170         hide : function(){
39171             this.focus.defer(10, this);
39172             var ml = this.menuListeners;
39173             this.menu.un("select", ml.select,  this);
39174             this.menu.un("show", ml.show,  this);
39175             this.menu.un("hide", ml.hide,  this);
39176         }
39177     },
39178     // private
39179     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39180     onTriggerClick : function(){
39181         if(this.disabled){
39182             return;
39183         }
39184         if(this.menu == null){
39185             this.menu = new Roo.menu.DateMenu();
39186            
39187         }
39188         
39189         Roo.apply(this.menu.picker,  {
39190             
39191             showClear: this.allowBlank,
39192             minDate : this.minValue,
39193             maxDate : this.maxValue,
39194             disabledDatesRE : this.ddMatch,
39195             disabledDatesText : this.disabledDatesText,
39196             
39197             format : this.useIso ? 'Y-m-d' : this.format,
39198             minText : String.format(this.minText, this.formatDate(this.minValue)),
39199             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39200             
39201         });
39202          this.menu.on(Roo.apply({}, this.menuListeners, {
39203             scope:this
39204         }));
39205        
39206         
39207         var m = this.menu;
39208         var p = m.picker;
39209         
39210         // hide month picker get's called when we called by 'before hide';
39211         
39212         var ignorehide = true;
39213         p.hideMonthPicker  = function(disableAnim){
39214             if (ignorehide) {
39215                 return;
39216             }
39217              if(this.monthPicker){
39218                 Roo.log("hideMonthPicker called");
39219                 if(disableAnim === true){
39220                     this.monthPicker.hide();
39221                 }else{
39222                     this.monthPicker.slideOut('t', {duration:.2});
39223                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39224                     p.fireEvent("select", this, this.value);
39225                     m.hide();
39226                 }
39227             }
39228         }
39229         
39230         Roo.log('picker set value');
39231         Roo.log(this.getValue());
39232         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39233         m.show(this.el, 'tl-bl?');
39234         ignorehide  = false;
39235         // this will trigger hideMonthPicker..
39236         
39237         
39238         // hidden the day picker
39239         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39240         
39241         
39242         
39243       
39244         
39245         p.showMonthPicker.defer(100, p);
39246     
39247         
39248        
39249     },
39250
39251     beforeBlur : function(){
39252         var v = this.parseDate(this.getRawValue());
39253         if(v){
39254             this.setValue(v);
39255         }
39256     }
39257
39258     /** @cfg {Boolean} grow @hide */
39259     /** @cfg {Number} growMin @hide */
39260     /** @cfg {Number} growMax @hide */
39261     /**
39262      * @hide
39263      * @method autoSize
39264      */
39265 });/*
39266  * Based on:
39267  * Ext JS Library 1.1.1
39268  * Copyright(c) 2006-2007, Ext JS, LLC.
39269  *
39270  * Originally Released Under LGPL - original licence link has changed is not relivant.
39271  *
39272  * Fork - LGPL
39273  * <script type="text/javascript">
39274  */
39275  
39276
39277 /**
39278  * @class Roo.form.ComboBox
39279  * @extends Roo.form.TriggerField
39280  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39281  * @constructor
39282  * Create a new ComboBox.
39283  * @param {Object} config Configuration options
39284  */
39285 Roo.form.ComboBox = function(config){
39286     Roo.form.ComboBox.superclass.constructor.call(this, config);
39287     this.addEvents({
39288         /**
39289          * @event expand
39290          * Fires when the dropdown list is expanded
39291              * @param {Roo.form.ComboBox} combo This combo box
39292              */
39293         'expand' : true,
39294         /**
39295          * @event collapse
39296          * Fires when the dropdown list is collapsed
39297              * @param {Roo.form.ComboBox} combo This combo box
39298              */
39299         'collapse' : true,
39300         /**
39301          * @event beforeselect
39302          * Fires before a list item is selected. Return false to cancel the selection.
39303              * @param {Roo.form.ComboBox} combo This combo box
39304              * @param {Roo.data.Record} record The data record returned from the underlying store
39305              * @param {Number} index The index of the selected item in the dropdown list
39306              */
39307         'beforeselect' : true,
39308         /**
39309          * @event select
39310          * Fires when a list item is selected
39311              * @param {Roo.form.ComboBox} combo This combo box
39312              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39313              * @param {Number} index The index of the selected item in the dropdown list
39314              */
39315         'select' : true,
39316         /**
39317          * @event beforequery
39318          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39319          * The event object passed has these properties:
39320              * @param {Roo.form.ComboBox} combo This combo box
39321              * @param {String} query The query
39322              * @param {Boolean} forceAll true to force "all" query
39323              * @param {Boolean} cancel true to cancel the query
39324              * @param {Object} e The query event object
39325              */
39326         'beforequery': true,
39327          /**
39328          * @event add
39329          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39330              * @param {Roo.form.ComboBox} combo This combo box
39331              */
39332         'add' : true,
39333         /**
39334          * @event edit
39335          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39336              * @param {Roo.form.ComboBox} combo This combo box
39337              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39338              */
39339         'edit' : true
39340         
39341         
39342     });
39343     if(this.transform){
39344         this.allowDomMove = false;
39345         var s = Roo.getDom(this.transform);
39346         if(!this.hiddenName){
39347             this.hiddenName = s.name;
39348         }
39349         if(!this.store){
39350             this.mode = 'local';
39351             var d = [], opts = s.options;
39352             for(var i = 0, len = opts.length;i < len; i++){
39353                 var o = opts[i];
39354                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39355                 if(o.selected) {
39356                     this.value = value;
39357                 }
39358                 d.push([value, o.text]);
39359             }
39360             this.store = new Roo.data.SimpleStore({
39361                 'id': 0,
39362                 fields: ['value', 'text'],
39363                 data : d
39364             });
39365             this.valueField = 'value';
39366             this.displayField = 'text';
39367         }
39368         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39369         if(!this.lazyRender){
39370             this.target = true;
39371             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39372             s.parentNode.removeChild(s); // remove it
39373             this.render(this.el.parentNode);
39374         }else{
39375             s.parentNode.removeChild(s); // remove it
39376         }
39377
39378     }
39379     if (this.store) {
39380         this.store = Roo.factory(this.store, Roo.data);
39381     }
39382     
39383     this.selectedIndex = -1;
39384     if(this.mode == 'local'){
39385         if(config.queryDelay === undefined){
39386             this.queryDelay = 10;
39387         }
39388         if(config.minChars === undefined){
39389             this.minChars = 0;
39390         }
39391     }
39392 };
39393
39394 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39395     /**
39396      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39397      */
39398     /**
39399      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39400      * rendering into an Roo.Editor, defaults to false)
39401      */
39402     /**
39403      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39404      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39405      */
39406     /**
39407      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39408      */
39409     /**
39410      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39411      * the dropdown list (defaults to undefined, with no header element)
39412      */
39413
39414      /**
39415      * @cfg {String/Roo.Template} tpl The template to use to render the output
39416      */
39417      
39418     // private
39419     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39420     /**
39421      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39422      */
39423     listWidth: undefined,
39424     /**
39425      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39426      * mode = 'remote' or 'text' if mode = 'local')
39427      */
39428     displayField: undefined,
39429     /**
39430      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39431      * mode = 'remote' or 'value' if mode = 'local'). 
39432      * Note: use of a valueField requires the user make a selection
39433      * in order for a value to be mapped.
39434      */
39435     valueField: undefined,
39436     
39437     
39438     /**
39439      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39440      * field's data value (defaults to the underlying DOM element's name)
39441      */
39442     hiddenName: undefined,
39443     /**
39444      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39445      */
39446     listClass: '',
39447     /**
39448      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39449      */
39450     selectedClass: 'x-combo-selected',
39451     /**
39452      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39453      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39454      * which displays a downward arrow icon).
39455      */
39456     triggerClass : 'x-form-arrow-trigger',
39457     /**
39458      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39459      */
39460     shadow:'sides',
39461     /**
39462      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39463      * anchor positions (defaults to 'tl-bl')
39464      */
39465     listAlign: 'tl-bl?',
39466     /**
39467      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39468      */
39469     maxHeight: 300,
39470     /**
39471      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39472      * query specified by the allQuery config option (defaults to 'query')
39473      */
39474     triggerAction: 'query',
39475     /**
39476      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39477      * (defaults to 4, does not apply if editable = false)
39478      */
39479     minChars : 4,
39480     /**
39481      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39482      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39483      */
39484     typeAhead: false,
39485     /**
39486      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39487      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39488      */
39489     queryDelay: 500,
39490     /**
39491      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39492      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39493      */
39494     pageSize: 0,
39495     /**
39496      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39497      * when editable = true (defaults to false)
39498      */
39499     selectOnFocus:false,
39500     /**
39501      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39502      */
39503     queryParam: 'query',
39504     /**
39505      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39506      * when mode = 'remote' (defaults to 'Loading...')
39507      */
39508     loadingText: 'Loading...',
39509     /**
39510      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39511      */
39512     resizable: false,
39513     /**
39514      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39515      */
39516     handleHeight : 8,
39517     /**
39518      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39519      * traditional select (defaults to true)
39520      */
39521     editable: true,
39522     /**
39523      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39524      */
39525     allQuery: '',
39526     /**
39527      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39528      */
39529     mode: 'remote',
39530     /**
39531      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39532      * listWidth has a higher value)
39533      */
39534     minListWidth : 70,
39535     /**
39536      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39537      * allow the user to set arbitrary text into the field (defaults to false)
39538      */
39539     forceSelection:false,
39540     /**
39541      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39542      * if typeAhead = true (defaults to 250)
39543      */
39544     typeAheadDelay : 250,
39545     /**
39546      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39547      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39548      */
39549     valueNotFoundText : undefined,
39550     /**
39551      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39552      */
39553     blockFocus : false,
39554     
39555     /**
39556      * @cfg {Boolean} disableClear Disable showing of clear button.
39557      */
39558     disableClear : false,
39559     /**
39560      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39561      */
39562     alwaysQuery : false,
39563     
39564     //private
39565     addicon : false,
39566     editicon: false,
39567     
39568     // element that contains real text value.. (when hidden is used..)
39569      
39570     // private
39571     onRender : function(ct, position){
39572         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39573         if(this.hiddenName){
39574             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39575                     'before', true);
39576             this.hiddenField.value =
39577                 this.hiddenValue !== undefined ? this.hiddenValue :
39578                 this.value !== undefined ? this.value : '';
39579
39580             // prevent input submission
39581             this.el.dom.removeAttribute('name');
39582              
39583              
39584         }
39585         if(Roo.isGecko){
39586             this.el.dom.setAttribute('autocomplete', 'off');
39587         }
39588
39589         var cls = 'x-combo-list';
39590
39591         this.list = new Roo.Layer({
39592             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39593         });
39594
39595         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39596         this.list.setWidth(lw);
39597         this.list.swallowEvent('mousewheel');
39598         this.assetHeight = 0;
39599
39600         if(this.title){
39601             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39602             this.assetHeight += this.header.getHeight();
39603         }
39604
39605         this.innerList = this.list.createChild({cls:cls+'-inner'});
39606         this.innerList.on('mouseover', this.onViewOver, this);
39607         this.innerList.on('mousemove', this.onViewMove, this);
39608         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39609         
39610         if(this.allowBlank && !this.pageSize && !this.disableClear){
39611             this.footer = this.list.createChild({cls:cls+'-ft'});
39612             this.pageTb = new Roo.Toolbar(this.footer);
39613            
39614         }
39615         if(this.pageSize){
39616             this.footer = this.list.createChild({cls:cls+'-ft'});
39617             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39618                     {pageSize: this.pageSize});
39619             
39620         }
39621         
39622         if (this.pageTb && this.allowBlank && !this.disableClear) {
39623             var _this = this;
39624             this.pageTb.add(new Roo.Toolbar.Fill(), {
39625                 cls: 'x-btn-icon x-btn-clear',
39626                 text: '&#160;',
39627                 handler: function()
39628                 {
39629                     _this.collapse();
39630                     _this.clearValue();
39631                     _this.onSelect(false, -1);
39632                 }
39633             });
39634         }
39635         if (this.footer) {
39636             this.assetHeight += this.footer.getHeight();
39637         }
39638         
39639
39640         if(!this.tpl){
39641             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39642         }
39643
39644         this.view = new Roo.View(this.innerList, this.tpl, {
39645             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39646         });
39647
39648         this.view.on('click', this.onViewClick, this);
39649
39650         this.store.on('beforeload', this.onBeforeLoad, this);
39651         this.store.on('load', this.onLoad, this);
39652         this.store.on('loadexception', this.onLoadException, this);
39653
39654         if(this.resizable){
39655             this.resizer = new Roo.Resizable(this.list,  {
39656                pinned:true, handles:'se'
39657             });
39658             this.resizer.on('resize', function(r, w, h){
39659                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39660                 this.listWidth = w;
39661                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39662                 this.restrictHeight();
39663             }, this);
39664             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39665         }
39666         if(!this.editable){
39667             this.editable = true;
39668             this.setEditable(false);
39669         }  
39670         
39671         
39672         if (typeof(this.events.add.listeners) != 'undefined') {
39673             
39674             this.addicon = this.wrap.createChild(
39675                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39676        
39677             this.addicon.on('click', function(e) {
39678                 this.fireEvent('add', this);
39679             }, this);
39680         }
39681         if (typeof(this.events.edit.listeners) != 'undefined') {
39682             
39683             this.editicon = this.wrap.createChild(
39684                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39685             if (this.addicon) {
39686                 this.editicon.setStyle('margin-left', '40px');
39687             }
39688             this.editicon.on('click', function(e) {
39689                 
39690                 // we fire even  if inothing is selected..
39691                 this.fireEvent('edit', this, this.lastData );
39692                 
39693             }, this);
39694         }
39695         
39696         
39697         
39698     },
39699
39700     // private
39701     initEvents : function(){
39702         Roo.form.ComboBox.superclass.initEvents.call(this);
39703
39704         this.keyNav = new Roo.KeyNav(this.el, {
39705             "up" : function(e){
39706                 this.inKeyMode = true;
39707                 this.selectPrev();
39708             },
39709
39710             "down" : function(e){
39711                 if(!this.isExpanded()){
39712                     this.onTriggerClick();
39713                 }else{
39714                     this.inKeyMode = true;
39715                     this.selectNext();
39716                 }
39717             },
39718
39719             "enter" : function(e){
39720                 this.onViewClick();
39721                 //return true;
39722             },
39723
39724             "esc" : function(e){
39725                 this.collapse();
39726             },
39727
39728             "tab" : function(e){
39729                 this.onViewClick(false);
39730                 this.fireEvent("specialkey", this, e);
39731                 return true;
39732             },
39733
39734             scope : this,
39735
39736             doRelay : function(foo, bar, hname){
39737                 if(hname == 'down' || this.scope.isExpanded()){
39738                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39739                 }
39740                 return true;
39741             },
39742
39743             forceKeyDown: true
39744         });
39745         this.queryDelay = Math.max(this.queryDelay || 10,
39746                 this.mode == 'local' ? 10 : 250);
39747         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39748         if(this.typeAhead){
39749             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39750         }
39751         if(this.editable !== false){
39752             this.el.on("keyup", this.onKeyUp, this);
39753         }
39754         if(this.forceSelection){
39755             this.on('blur', this.doForce, this);
39756         }
39757     },
39758
39759     onDestroy : function(){
39760         if(this.view){
39761             this.view.setStore(null);
39762             this.view.el.removeAllListeners();
39763             this.view.el.remove();
39764             this.view.purgeListeners();
39765         }
39766         if(this.list){
39767             this.list.destroy();
39768         }
39769         if(this.store){
39770             this.store.un('beforeload', this.onBeforeLoad, this);
39771             this.store.un('load', this.onLoad, this);
39772             this.store.un('loadexception', this.onLoadException, this);
39773         }
39774         Roo.form.ComboBox.superclass.onDestroy.call(this);
39775     },
39776
39777     // private
39778     fireKey : function(e){
39779         if(e.isNavKeyPress() && !this.list.isVisible()){
39780             this.fireEvent("specialkey", this, e);
39781         }
39782     },
39783
39784     // private
39785     onResize: function(w, h){
39786         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39787         
39788         if(typeof w != 'number'){
39789             // we do not handle it!?!?
39790             return;
39791         }
39792         var tw = this.trigger.getWidth();
39793         tw += this.addicon ? this.addicon.getWidth() : 0;
39794         tw += this.editicon ? this.editicon.getWidth() : 0;
39795         var x = w - tw;
39796         this.el.setWidth( this.adjustWidth('input', x));
39797             
39798         this.trigger.setStyle('left', x+'px');
39799         
39800         if(this.list && this.listWidth === undefined){
39801             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39802             this.list.setWidth(lw);
39803             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39804         }
39805         
39806     
39807         
39808     },
39809
39810     /**
39811      * Allow or prevent the user from directly editing the field text.  If false is passed,
39812      * the user will only be able to select from the items defined in the dropdown list.  This method
39813      * is the runtime equivalent of setting the 'editable' config option at config time.
39814      * @param {Boolean} value True to allow the user to directly edit the field text
39815      */
39816     setEditable : function(value){
39817         if(value == this.editable){
39818             return;
39819         }
39820         this.editable = value;
39821         if(!value){
39822             this.el.dom.setAttribute('readOnly', true);
39823             this.el.on('mousedown', this.onTriggerClick,  this);
39824             this.el.addClass('x-combo-noedit');
39825         }else{
39826             this.el.dom.setAttribute('readOnly', false);
39827             this.el.un('mousedown', this.onTriggerClick,  this);
39828             this.el.removeClass('x-combo-noedit');
39829         }
39830     },
39831
39832     // private
39833     onBeforeLoad : function(){
39834         if(!this.hasFocus){
39835             return;
39836         }
39837         this.innerList.update(this.loadingText ?
39838                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39839         this.restrictHeight();
39840         this.selectedIndex = -1;
39841     },
39842
39843     // private
39844     onLoad : function(){
39845         if(!this.hasFocus){
39846             return;
39847         }
39848         if(this.store.getCount() > 0){
39849             this.expand();
39850             this.restrictHeight();
39851             if(this.lastQuery == this.allQuery){
39852                 if(this.editable){
39853                     this.el.dom.select();
39854                 }
39855                 if(!this.selectByValue(this.value, true)){
39856                     this.select(0, true);
39857                 }
39858             }else{
39859                 this.selectNext();
39860                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39861                     this.taTask.delay(this.typeAheadDelay);
39862                 }
39863             }
39864         }else{
39865             this.onEmptyResults();
39866         }
39867         //this.el.focus();
39868     },
39869     // private
39870     onLoadException : function()
39871     {
39872         this.collapse();
39873         Roo.log(this.store.reader.jsonData);
39874         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39875             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39876         }
39877         
39878         
39879     },
39880     // private
39881     onTypeAhead : function(){
39882         if(this.store.getCount() > 0){
39883             var r = this.store.getAt(0);
39884             var newValue = r.data[this.displayField];
39885             var len = newValue.length;
39886             var selStart = this.getRawValue().length;
39887             if(selStart != len){
39888                 this.setRawValue(newValue);
39889                 this.selectText(selStart, newValue.length);
39890             }
39891         }
39892     },
39893
39894     // private
39895     onSelect : function(record, index){
39896         if(this.fireEvent('beforeselect', this, record, index) !== false){
39897             this.setFromData(index > -1 ? record.data : false);
39898             this.collapse();
39899             this.fireEvent('select', this, record, index);
39900         }
39901     },
39902
39903     /**
39904      * Returns the currently selected field value or empty string if no value is set.
39905      * @return {String} value The selected value
39906      */
39907     getValue : function(){
39908         if(this.valueField){
39909             return typeof this.value != 'undefined' ? this.value : '';
39910         }
39911         return Roo.form.ComboBox.superclass.getValue.call(this);
39912     },
39913
39914     /**
39915      * Clears any text/value currently set in the field
39916      */
39917     clearValue : function(){
39918         if(this.hiddenField){
39919             this.hiddenField.value = '';
39920         }
39921         this.value = '';
39922         this.setRawValue('');
39923         this.lastSelectionText = '';
39924         
39925     },
39926
39927     /**
39928      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39929      * will be displayed in the field.  If the value does not match the data value of an existing item,
39930      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39931      * Otherwise the field will be blank (although the value will still be set).
39932      * @param {String} value The value to match
39933      */
39934     setValue : function(v){
39935         var text = v;
39936         if(this.valueField){
39937             var r = this.findRecord(this.valueField, v);
39938             if(r){
39939                 text = r.data[this.displayField];
39940             }else if(this.valueNotFoundText !== undefined){
39941                 text = this.valueNotFoundText;
39942             }
39943         }
39944         this.lastSelectionText = text;
39945         if(this.hiddenField){
39946             this.hiddenField.value = v;
39947         }
39948         Roo.form.ComboBox.superclass.setValue.call(this, text);
39949         this.value = v;
39950     },
39951     /**
39952      * @property {Object} the last set data for the element
39953      */
39954     
39955     lastData : false,
39956     /**
39957      * Sets the value of the field based on a object which is related to the record format for the store.
39958      * @param {Object} value the value to set as. or false on reset?
39959      */
39960     setFromData : function(o){
39961         var dv = ''; // display value
39962         var vv = ''; // value value..
39963         this.lastData = o;
39964         if (this.displayField) {
39965             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39966         } else {
39967             // this is an error condition!!!
39968             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39969         }
39970         
39971         if(this.valueField){
39972             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39973         }
39974         if(this.hiddenField){
39975             this.hiddenField.value = vv;
39976             
39977             this.lastSelectionText = dv;
39978             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39979             this.value = vv;
39980             return;
39981         }
39982         // no hidden field.. - we store the value in 'value', but still display
39983         // display field!!!!
39984         this.lastSelectionText = dv;
39985         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39986         this.value = vv;
39987         
39988         
39989     },
39990     // private
39991     reset : function(){
39992         // overridden so that last data is reset..
39993         this.setValue(this.resetValue);
39994         this.clearInvalid();
39995         this.lastData = false;
39996         if (this.view) {
39997             this.view.clearSelections();
39998         }
39999     },
40000     // private
40001     findRecord : function(prop, value){
40002         var record;
40003         if(this.store.getCount() > 0){
40004             this.store.each(function(r){
40005                 if(r.data[prop] == value){
40006                     record = r;
40007                     return false;
40008                 }
40009                 return true;
40010             });
40011         }
40012         return record;
40013     },
40014     
40015     getName: function()
40016     {
40017         // returns hidden if it's set..
40018         if (!this.rendered) {return ''};
40019         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40020         
40021     },
40022     // private
40023     onViewMove : function(e, t){
40024         this.inKeyMode = false;
40025     },
40026
40027     // private
40028     onViewOver : function(e, t){
40029         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40030             return;
40031         }
40032         var item = this.view.findItemFromChild(t);
40033         if(item){
40034             var index = this.view.indexOf(item);
40035             this.select(index, false);
40036         }
40037     },
40038
40039     // private
40040     onViewClick : function(doFocus)
40041     {
40042         var index = this.view.getSelectedIndexes()[0];
40043         var r = this.store.getAt(index);
40044         if(r){
40045             this.onSelect(r, index);
40046         }
40047         if(doFocus !== false && !this.blockFocus){
40048             this.el.focus();
40049         }
40050     },
40051
40052     // private
40053     restrictHeight : function(){
40054         this.innerList.dom.style.height = '';
40055         var inner = this.innerList.dom;
40056         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40057         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40058         this.list.beginUpdate();
40059         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40060         this.list.alignTo(this.el, this.listAlign);
40061         this.list.endUpdate();
40062     },
40063
40064     // private
40065     onEmptyResults : function(){
40066         this.collapse();
40067     },
40068
40069     /**
40070      * Returns true if the dropdown list is expanded, else false.
40071      */
40072     isExpanded : function(){
40073         return this.list.isVisible();
40074     },
40075
40076     /**
40077      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40078      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40079      * @param {String} value The data value of the item to select
40080      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40081      * selected item if it is not currently in view (defaults to true)
40082      * @return {Boolean} True if the value matched an item in the list, else false
40083      */
40084     selectByValue : function(v, scrollIntoView){
40085         if(v !== undefined && v !== null){
40086             var r = this.findRecord(this.valueField || this.displayField, v);
40087             if(r){
40088                 this.select(this.store.indexOf(r), scrollIntoView);
40089                 return true;
40090             }
40091         }
40092         return false;
40093     },
40094
40095     /**
40096      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40097      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40098      * @param {Number} index The zero-based index of the list item to select
40099      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40100      * selected item if it is not currently in view (defaults to true)
40101      */
40102     select : function(index, scrollIntoView){
40103         this.selectedIndex = index;
40104         this.view.select(index);
40105         if(scrollIntoView !== false){
40106             var el = this.view.getNode(index);
40107             if(el){
40108                 this.innerList.scrollChildIntoView(el, false);
40109             }
40110         }
40111     },
40112
40113     // private
40114     selectNext : function(){
40115         var ct = this.store.getCount();
40116         if(ct > 0){
40117             if(this.selectedIndex == -1){
40118                 this.select(0);
40119             }else if(this.selectedIndex < ct-1){
40120                 this.select(this.selectedIndex+1);
40121             }
40122         }
40123     },
40124
40125     // private
40126     selectPrev : function(){
40127         var ct = this.store.getCount();
40128         if(ct > 0){
40129             if(this.selectedIndex == -1){
40130                 this.select(0);
40131             }else if(this.selectedIndex != 0){
40132                 this.select(this.selectedIndex-1);
40133             }
40134         }
40135     },
40136
40137     // private
40138     onKeyUp : function(e){
40139         if(this.editable !== false && !e.isSpecialKey()){
40140             this.lastKey = e.getKey();
40141             this.dqTask.delay(this.queryDelay);
40142         }
40143     },
40144
40145     // private
40146     validateBlur : function(){
40147         return !this.list || !this.list.isVisible();   
40148     },
40149
40150     // private
40151     initQuery : function(){
40152         this.doQuery(this.getRawValue());
40153     },
40154
40155     // private
40156     doForce : function(){
40157         if(this.el.dom.value.length > 0){
40158             this.el.dom.value =
40159                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40160              
40161         }
40162     },
40163
40164     /**
40165      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40166      * query allowing the query action to be canceled if needed.
40167      * @param {String} query The SQL query to execute
40168      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40169      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40170      * saved in the current store (defaults to false)
40171      */
40172     doQuery : function(q, forceAll){
40173         if(q === undefined || q === null){
40174             q = '';
40175         }
40176         var qe = {
40177             query: q,
40178             forceAll: forceAll,
40179             combo: this,
40180             cancel:false
40181         };
40182         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40183             return false;
40184         }
40185         q = qe.query;
40186         forceAll = qe.forceAll;
40187         if(forceAll === true || (q.length >= this.minChars)){
40188             if(this.lastQuery != q || this.alwaysQuery){
40189                 this.lastQuery = q;
40190                 if(this.mode == 'local'){
40191                     this.selectedIndex = -1;
40192                     if(forceAll){
40193                         this.store.clearFilter();
40194                     }else{
40195                         this.store.filter(this.displayField, q);
40196                     }
40197                     this.onLoad();
40198                 }else{
40199                     this.store.baseParams[this.queryParam] = q;
40200                     this.store.load({
40201                         params: this.getParams(q)
40202                     });
40203                     this.expand();
40204                 }
40205             }else{
40206                 this.selectedIndex = -1;
40207                 this.onLoad();   
40208             }
40209         }
40210     },
40211
40212     // private
40213     getParams : function(q){
40214         var p = {};
40215         //p[this.queryParam] = q;
40216         if(this.pageSize){
40217             p.start = 0;
40218             p.limit = this.pageSize;
40219         }
40220         return p;
40221     },
40222
40223     /**
40224      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40225      */
40226     collapse : function(){
40227         if(!this.isExpanded()){
40228             return;
40229         }
40230         this.list.hide();
40231         Roo.get(document).un('mousedown', this.collapseIf, this);
40232         Roo.get(document).un('mousewheel', this.collapseIf, this);
40233         if (!this.editable) {
40234             Roo.get(document).un('keydown', this.listKeyPress, this);
40235         }
40236         this.fireEvent('collapse', this);
40237     },
40238
40239     // private
40240     collapseIf : function(e){
40241         if(!e.within(this.wrap) && !e.within(this.list)){
40242             this.collapse();
40243         }
40244     },
40245
40246     /**
40247      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40248      */
40249     expand : function(){
40250         if(this.isExpanded() || !this.hasFocus){
40251             return;
40252         }
40253         this.list.alignTo(this.el, this.listAlign);
40254         this.list.show();
40255         Roo.get(document).on('mousedown', this.collapseIf, this);
40256         Roo.get(document).on('mousewheel', this.collapseIf, this);
40257         if (!this.editable) {
40258             Roo.get(document).on('keydown', this.listKeyPress, this);
40259         }
40260         
40261         this.fireEvent('expand', this);
40262     },
40263
40264     // private
40265     // Implements the default empty TriggerField.onTriggerClick function
40266     onTriggerClick : function(){
40267         if(this.disabled){
40268             return;
40269         }
40270         if(this.isExpanded()){
40271             this.collapse();
40272             if (!this.blockFocus) {
40273                 this.el.focus();
40274             }
40275             
40276         }else {
40277             this.hasFocus = true;
40278             if(this.triggerAction == 'all') {
40279                 this.doQuery(this.allQuery, true);
40280             } else {
40281                 this.doQuery(this.getRawValue());
40282             }
40283             if (!this.blockFocus) {
40284                 this.el.focus();
40285             }
40286         }
40287     },
40288     listKeyPress : function(e)
40289     {
40290         //Roo.log('listkeypress');
40291         // scroll to first matching element based on key pres..
40292         if (e.isSpecialKey()) {
40293             return false;
40294         }
40295         var k = String.fromCharCode(e.getKey()).toUpperCase();
40296         //Roo.log(k);
40297         var match  = false;
40298         var csel = this.view.getSelectedNodes();
40299         var cselitem = false;
40300         if (csel.length) {
40301             var ix = this.view.indexOf(csel[0]);
40302             cselitem  = this.store.getAt(ix);
40303             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40304                 cselitem = false;
40305             }
40306             
40307         }
40308         
40309         this.store.each(function(v) { 
40310             if (cselitem) {
40311                 // start at existing selection.
40312                 if (cselitem.id == v.id) {
40313                     cselitem = false;
40314                 }
40315                 return;
40316             }
40317                 
40318             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40319                 match = this.store.indexOf(v);
40320                 return false;
40321             }
40322         }, this);
40323         
40324         if (match === false) {
40325             return true; // no more action?
40326         }
40327         // scroll to?
40328         this.view.select(match);
40329         var sn = Roo.get(this.view.getSelectedNodes()[0])
40330         sn.scrollIntoView(sn.dom.parentNode, false);
40331     }
40332
40333     /** 
40334     * @cfg {Boolean} grow 
40335     * @hide 
40336     */
40337     /** 
40338     * @cfg {Number} growMin 
40339     * @hide 
40340     */
40341     /** 
40342     * @cfg {Number} growMax 
40343     * @hide 
40344     */
40345     /**
40346      * @hide
40347      * @method autoSize
40348      */
40349 });/*
40350  * Copyright(c) 2010-2012, Roo J Solutions Limited
40351  *
40352  * Licence LGPL
40353  *
40354  */
40355
40356 /**
40357  * @class Roo.form.ComboBoxArray
40358  * @extends Roo.form.TextField
40359  * A facebook style adder... for lists of email / people / countries  etc...
40360  * pick multiple items from a combo box, and shows each one.
40361  *
40362  *  Fred [x]  Brian [x]  [Pick another |v]
40363  *
40364  *
40365  *  For this to work: it needs various extra information
40366  *    - normal combo problay has
40367  *      name, hiddenName
40368  *    + displayField, valueField
40369  *
40370  *    For our purpose...
40371  *
40372  *
40373  *   If we change from 'extends' to wrapping...
40374  *   
40375  *  
40376  *
40377  
40378  
40379  * @constructor
40380  * Create a new ComboBoxArray.
40381  * @param {Object} config Configuration options
40382  */
40383  
40384
40385 Roo.form.ComboBoxArray = function(config)
40386 {
40387     this.addEvents({
40388         /**
40389          * @event beforeremove
40390          * Fires before remove the value from the list
40391              * @param {Roo.form.ComboBoxArray} _self This combo box array
40392              * @param {Roo.form.ComboBoxArray.Item} item removed item
40393              */
40394         'beforeremove' : true,
40395         /**
40396          * @event remove
40397          * Fires when remove the value from the list
40398              * @param {Roo.form.ComboBoxArray} _self This combo box array
40399              * @param {Roo.form.ComboBoxArray.Item} item removed item
40400              */
40401         'remove' : true
40402         
40403         
40404     });
40405     
40406     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40407     
40408     this.items = new Roo.util.MixedCollection(false);
40409     
40410     // construct the child combo...
40411     
40412     
40413     
40414     
40415    
40416     
40417 }
40418
40419  
40420 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40421
40422     /**
40423      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40424      */
40425     
40426     lastData : false,
40427     
40428     // behavies liek a hiddne field
40429     inputType:      'hidden',
40430     /**
40431      * @cfg {Number} width The width of the box that displays the selected element
40432      */ 
40433     width:          300,
40434
40435     
40436     
40437     /**
40438      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40439      */
40440     name : false,
40441     /**
40442      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40443      */
40444     hiddenName : false,
40445     
40446     
40447     // private the array of items that are displayed..
40448     items  : false,
40449     // private - the hidden field el.
40450     hiddenEl : false,
40451     // private - the filed el..
40452     el : false,
40453     
40454     //validateValue : function() { return true; }, // all values are ok!
40455     //onAddClick: function() { },
40456     
40457     onRender : function(ct, position) 
40458     {
40459         
40460         // create the standard hidden element
40461         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40462         
40463         
40464         // give fake names to child combo;
40465         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40466         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40467         
40468         this.combo = Roo.factory(this.combo, Roo.form);
40469         this.combo.onRender(ct, position);
40470         if (typeof(this.combo.width) != 'undefined') {
40471             this.combo.onResize(this.combo.width,0);
40472         }
40473         
40474         this.combo.initEvents();
40475         
40476         // assigned so form know we need to do this..
40477         this.store          = this.combo.store;
40478         this.valueField     = this.combo.valueField;
40479         this.displayField   = this.combo.displayField ;
40480         
40481         
40482         this.combo.wrap.addClass('x-cbarray-grp');
40483         
40484         var cbwrap = this.combo.wrap.createChild(
40485             {tag: 'div', cls: 'x-cbarray-cb'},
40486             this.combo.el.dom
40487         );
40488         
40489              
40490         this.hiddenEl = this.combo.wrap.createChild({
40491             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40492         });
40493         this.el = this.combo.wrap.createChild({
40494             tag: 'input',  type:'hidden' , name: this.name, value : ''
40495         });
40496          //   this.el.dom.removeAttribute("name");
40497         
40498         
40499         this.outerWrap = this.combo.wrap;
40500         this.wrap = cbwrap;
40501         
40502         this.outerWrap.setWidth(this.width);
40503         this.outerWrap.dom.removeChild(this.el.dom);
40504         
40505         this.wrap.dom.appendChild(this.el.dom);
40506         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40507         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40508         
40509         this.combo.trigger.setStyle('position','relative');
40510         this.combo.trigger.setStyle('left', '0px');
40511         this.combo.trigger.setStyle('top', '2px');
40512         
40513         this.combo.el.setStyle('vertical-align', 'text-bottom');
40514         
40515         //this.trigger.setStyle('vertical-align', 'top');
40516         
40517         // this should use the code from combo really... on('add' ....)
40518         if (this.adder) {
40519             
40520         
40521             this.adder = this.outerWrap.createChild(
40522                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40523             var _t = this;
40524             this.adder.on('click', function(e) {
40525                 _t.fireEvent('adderclick', this, e);
40526             }, _t);
40527         }
40528         //var _t = this;
40529         //this.adder.on('click', this.onAddClick, _t);
40530         
40531         
40532         this.combo.on('select', function(cb, rec, ix) {
40533             this.addItem(rec.data);
40534             
40535             cb.setValue('');
40536             cb.el.dom.value = '';
40537             //cb.lastData = rec.data;
40538             // add to list
40539             
40540         }, this);
40541         
40542         
40543     },
40544     
40545     
40546     getName: function()
40547     {
40548         // returns hidden if it's set..
40549         if (!this.rendered) {return ''};
40550         return  this.hiddenName ? this.hiddenName : this.name;
40551         
40552     },
40553     
40554     
40555     onResize: function(w, h){
40556         
40557         return;
40558         // not sure if this is needed..
40559         //this.combo.onResize(w,h);
40560         
40561         if(typeof w != 'number'){
40562             // we do not handle it!?!?
40563             return;
40564         }
40565         var tw = this.combo.trigger.getWidth();
40566         tw += this.addicon ? this.addicon.getWidth() : 0;
40567         tw += this.editicon ? this.editicon.getWidth() : 0;
40568         var x = w - tw;
40569         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40570             
40571         this.combo.trigger.setStyle('left', '0px');
40572         
40573         if(this.list && this.listWidth === undefined){
40574             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40575             this.list.setWidth(lw);
40576             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40577         }
40578         
40579     
40580         
40581     },
40582     
40583     addItem: function(rec)
40584     {
40585         var valueField = this.combo.valueField;
40586         var displayField = this.combo.displayField;
40587         if (this.items.indexOfKey(rec[valueField]) > -1) {
40588             //console.log("GOT " + rec.data.id);
40589             return;
40590         }
40591         
40592         var x = new Roo.form.ComboBoxArray.Item({
40593             //id : rec[this.idField],
40594             data : rec,
40595             displayField : displayField ,
40596             tipField : displayField ,
40597             cb : this
40598         });
40599         // use the 
40600         this.items.add(rec[valueField],x);
40601         // add it before the element..
40602         this.updateHiddenEl();
40603         x.render(this.outerWrap, this.wrap.dom);
40604         // add the image handler..
40605     },
40606     
40607     updateHiddenEl : function()
40608     {
40609         this.validate();
40610         if (!this.hiddenEl) {
40611             return;
40612         }
40613         var ar = [];
40614         var idField = this.combo.valueField;
40615         
40616         this.items.each(function(f) {
40617             ar.push(f.data[idField]);
40618            
40619         });
40620         this.hiddenEl.dom.value = ar.join(',');
40621         this.validate();
40622     },
40623     
40624     reset : function()
40625     {
40626         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40627         this.items.each(function(f) {
40628            f.remove(); 
40629         });
40630         this.el.dom.value = '';
40631         if (this.hiddenEl) {
40632             this.hiddenEl.dom.value = '';
40633         }
40634         
40635     },
40636     getValue: function()
40637     {
40638         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40639     },
40640     setValue: function(v) // not a valid action - must use addItems..
40641     {
40642          
40643         this.reset();
40644         
40645         
40646         
40647         if (this.store.isLocal && (typeof(v) == 'string')) {
40648             // then we can use the store to find the values..
40649             // comma seperated at present.. this needs to allow JSON based encoding..
40650             this.hiddenEl.value  = v;
40651             var v_ar = [];
40652             Roo.each(v.split(','), function(k) {
40653                 Roo.log("CHECK " + this.valueField + ',' + k);
40654                 var li = this.store.query(this.valueField, k);
40655                 if (!li.length) {
40656                     return;
40657                 }
40658                 var add = {};
40659                 add[this.valueField] = k;
40660                 add[this.displayField] = li.item(0).data[this.displayField];
40661                 
40662                 this.addItem(add);
40663             }, this) 
40664              
40665         }
40666         if (typeof(v) == 'object' ) {
40667             // then let's assume it's an array of objects..
40668             Roo.each(v, function(l) {
40669                 this.addItem(l);
40670             }, this);
40671              
40672         }
40673         
40674         
40675     },
40676     setFromData: function(v)
40677     {
40678         // this recieves an object, if setValues is called.
40679         this.reset();
40680         this.el.dom.value = v[this.displayField];
40681         this.hiddenEl.dom.value = v[this.valueField];
40682         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40683             return;
40684         }
40685         var kv = v[this.valueField];
40686         var dv = v[this.displayField];
40687         kv = typeof(kv) != 'string' ? '' : kv;
40688         dv = typeof(dv) != 'string' ? '' : dv;
40689         
40690         
40691         var keys = kv.split(',');
40692         var display = dv.split(',');
40693         for (var i = 0 ; i < keys.length; i++) {
40694             
40695             add = {};
40696             add[this.valueField] = keys[i];
40697             add[this.displayField] = display[i];
40698             this.addItem(add);
40699         }
40700       
40701         
40702     },
40703     
40704     /**
40705      * Validates the combox array value
40706      * @return {Boolean} True if the value is valid, else false
40707      */
40708     validate : function(){
40709         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40710             this.clearInvalid();
40711             return true;
40712         }
40713         return false;
40714     },
40715     
40716     validateValue : function(value){
40717         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40718         
40719     },
40720     
40721     /*@
40722      * overide
40723      * 
40724      */
40725     isDirty : function() {
40726         if(this.disabled) {
40727             return false;
40728         }
40729         
40730         try {
40731             var d = Roo.decode(String(this.originalValue));
40732         } catch (e) {
40733             return String(this.getValue()) !== String(this.originalValue);
40734         }
40735         
40736         var originalValue = [];
40737         
40738         for (var i = 0; i < d.length; i++){
40739             originalValue.push(d[i][this.valueField]);
40740         }
40741         
40742         return String(this.getValue()) !== String(originalValue.join(','));
40743         
40744     }
40745     
40746 });
40747
40748
40749
40750 /**
40751  * @class Roo.form.ComboBoxArray.Item
40752  * @extends Roo.BoxComponent
40753  * A selected item in the list
40754  *  Fred [x]  Brian [x]  [Pick another |v]
40755  * 
40756  * @constructor
40757  * Create a new item.
40758  * @param {Object} config Configuration options
40759  */
40760  
40761 Roo.form.ComboBoxArray.Item = function(config) {
40762     config.id = Roo.id();
40763     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40764 }
40765
40766 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40767     data : {},
40768     cb: false,
40769     displayField : false,
40770     tipField : false,
40771     
40772     
40773     defaultAutoCreate : {
40774         tag: 'div',
40775         cls: 'x-cbarray-item',
40776         cn : [ 
40777             { tag: 'div' },
40778             {
40779                 tag: 'img',
40780                 width:16,
40781                 height : 16,
40782                 src : Roo.BLANK_IMAGE_URL ,
40783                 align: 'center'
40784             }
40785         ]
40786         
40787     },
40788     
40789  
40790     onRender : function(ct, position)
40791     {
40792         Roo.form.Field.superclass.onRender.call(this, ct, position);
40793         
40794         if(!this.el){
40795             var cfg = this.getAutoCreate();
40796             this.el = ct.createChild(cfg, position);
40797         }
40798         
40799         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40800         
40801         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40802             this.cb.renderer(this.data) :
40803             String.format('{0}',this.data[this.displayField]);
40804         
40805             
40806         this.el.child('div').dom.setAttribute('qtip',
40807                         String.format('{0}',this.data[this.tipField])
40808         );
40809         
40810         this.el.child('img').on('click', this.remove, this);
40811         
40812     },
40813    
40814     remove : function()
40815     {
40816         if(this.cb.disabled){
40817             return;
40818         }
40819         
40820         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40821             this.cb.items.remove(this);
40822             this.el.child('img').un('click', this.remove, this);
40823             this.el.remove();
40824             this.cb.updateHiddenEl();
40825
40826             this.cb.fireEvent('remove', this.cb, this);
40827         }
40828         
40829     }
40830 });/*
40831  * Based on:
40832  * Ext JS Library 1.1.1
40833  * Copyright(c) 2006-2007, Ext JS, LLC.
40834  *
40835  * Originally Released Under LGPL - original licence link has changed is not relivant.
40836  *
40837  * Fork - LGPL
40838  * <script type="text/javascript">
40839  */
40840 /**
40841  * @class Roo.form.Checkbox
40842  * @extends Roo.form.Field
40843  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40844  * @constructor
40845  * Creates a new Checkbox
40846  * @param {Object} config Configuration options
40847  */
40848 Roo.form.Checkbox = function(config){
40849     Roo.form.Checkbox.superclass.constructor.call(this, config);
40850     this.addEvents({
40851         /**
40852          * @event check
40853          * Fires when the checkbox is checked or unchecked.
40854              * @param {Roo.form.Checkbox} this This checkbox
40855              * @param {Boolean} checked The new checked value
40856              */
40857         check : true
40858     });
40859 };
40860
40861 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40862     /**
40863      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40864      */
40865     focusClass : undefined,
40866     /**
40867      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40868      */
40869     fieldClass: "x-form-field",
40870     /**
40871      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40872      */
40873     checked: false,
40874     /**
40875      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40876      * {tag: "input", type: "checkbox", autocomplete: "off"})
40877      */
40878     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40879     /**
40880      * @cfg {String} boxLabel The text that appears beside the checkbox
40881      */
40882     boxLabel : "",
40883     /**
40884      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40885      */  
40886     inputValue : '1',
40887     /**
40888      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40889      */
40890      valueOff: '0', // value when not checked..
40891
40892     actionMode : 'viewEl', 
40893     //
40894     // private
40895     itemCls : 'x-menu-check-item x-form-item',
40896     groupClass : 'x-menu-group-item',
40897     inputType : 'hidden',
40898     
40899     
40900     inSetChecked: false, // check that we are not calling self...
40901     
40902     inputElement: false, // real input element?
40903     basedOn: false, // ????
40904     
40905     isFormField: true, // not sure where this is needed!!!!
40906
40907     onResize : function(){
40908         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40909         if(!this.boxLabel){
40910             this.el.alignTo(this.wrap, 'c-c');
40911         }
40912     },
40913
40914     initEvents : function(){
40915         Roo.form.Checkbox.superclass.initEvents.call(this);
40916         this.el.on("click", this.onClick,  this);
40917         this.el.on("change", this.onClick,  this);
40918     },
40919
40920
40921     getResizeEl : function(){
40922         return this.wrap;
40923     },
40924
40925     getPositionEl : function(){
40926         return this.wrap;
40927     },
40928
40929     // private
40930     onRender : function(ct, position){
40931         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40932         /*
40933         if(this.inputValue !== undefined){
40934             this.el.dom.value = this.inputValue;
40935         }
40936         */
40937         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40938         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40939         var viewEl = this.wrap.createChild({ 
40940             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40941         this.viewEl = viewEl;   
40942         this.wrap.on('click', this.onClick,  this); 
40943         
40944         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40945         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40946         
40947         
40948         
40949         if(this.boxLabel){
40950             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40951         //    viewEl.on('click', this.onClick,  this); 
40952         }
40953         //if(this.checked){
40954             this.setChecked(this.checked);
40955         //}else{
40956             //this.checked = this.el.dom;
40957         //}
40958
40959     },
40960
40961     // private
40962     initValue : Roo.emptyFn,
40963
40964     /**
40965      * Returns the checked state of the checkbox.
40966      * @return {Boolean} True if checked, else false
40967      */
40968     getValue : function(){
40969         if(this.el){
40970             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40971         }
40972         return this.valueOff;
40973         
40974     },
40975
40976         // private
40977     onClick : function(){ 
40978         if (this.disabled) {
40979             return;
40980         }
40981         this.setChecked(!this.checked);
40982
40983         //if(this.el.dom.checked != this.checked){
40984         //    this.setValue(this.el.dom.checked);
40985        // }
40986     },
40987
40988     /**
40989      * Sets the checked state of the checkbox.
40990      * On is always based on a string comparison between inputValue and the param.
40991      * @param {Boolean/String} value - the value to set 
40992      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40993      */
40994     setValue : function(v,suppressEvent){
40995         
40996         
40997         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40998         //if(this.el && this.el.dom){
40999         //    this.el.dom.checked = this.checked;
41000         //    this.el.dom.defaultChecked = this.checked;
41001         //}
41002         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
41003         //this.fireEvent("check", this, this.checked);
41004     },
41005     // private..
41006     setChecked : function(state,suppressEvent)
41007     {
41008         if (this.inSetChecked) {
41009             this.checked = state;
41010             return;
41011         }
41012         
41013     
41014         if(this.wrap){
41015             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41016         }
41017         this.checked = state;
41018         if(suppressEvent !== true){
41019             this.fireEvent('check', this, state);
41020         }
41021         this.inSetChecked = true;
41022         this.el.dom.value = state ? this.inputValue : this.valueOff;
41023         this.inSetChecked = false;
41024         
41025     },
41026     // handle setting of hidden value by some other method!!?!?
41027     setFromHidden: function()
41028     {
41029         if(!this.el){
41030             return;
41031         }
41032         //console.log("SET FROM HIDDEN");
41033         //alert('setFrom hidden');
41034         this.setValue(this.el.dom.value);
41035     },
41036     
41037     onDestroy : function()
41038     {
41039         if(this.viewEl){
41040             Roo.get(this.viewEl).remove();
41041         }
41042          
41043         Roo.form.Checkbox.superclass.onDestroy.call(this);
41044     }
41045
41046 });/*
41047  * Based on:
41048  * Ext JS Library 1.1.1
41049  * Copyright(c) 2006-2007, Ext JS, LLC.
41050  *
41051  * Originally Released Under LGPL - original licence link has changed is not relivant.
41052  *
41053  * Fork - LGPL
41054  * <script type="text/javascript">
41055  */
41056  
41057 /**
41058  * @class Roo.form.Radio
41059  * @extends Roo.form.Checkbox
41060  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41061  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41062  * @constructor
41063  * Creates a new Radio
41064  * @param {Object} config Configuration options
41065  */
41066 Roo.form.Radio = function(){
41067     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41068 };
41069 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41070     inputType: 'radio',
41071
41072     /**
41073      * If this radio is part of a group, it will return the selected value
41074      * @return {String}
41075      */
41076     getGroupValue : function(){
41077         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41078     },
41079     
41080     
41081     onRender : function(ct, position){
41082         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41083         
41084         if(this.inputValue !== undefined){
41085             this.el.dom.value = this.inputValue;
41086         }
41087          
41088         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41089         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41090         //var viewEl = this.wrap.createChild({ 
41091         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41092         //this.viewEl = viewEl;   
41093         //this.wrap.on('click', this.onClick,  this); 
41094         
41095         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41096         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41097         
41098         
41099         
41100         if(this.boxLabel){
41101             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41102         //    viewEl.on('click', this.onClick,  this); 
41103         }
41104          if(this.checked){
41105             this.el.dom.checked =   'checked' ;
41106         }
41107          
41108     } 
41109     
41110     
41111 });//<script type="text/javascript">
41112
41113 /*
41114  * Based  Ext JS Library 1.1.1
41115  * Copyright(c) 2006-2007, Ext JS, LLC.
41116  * LGPL
41117  *
41118  */
41119  
41120 /**
41121  * @class Roo.HtmlEditorCore
41122  * @extends Roo.Component
41123  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41124  *
41125  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41126  */
41127
41128 Roo.HtmlEditorCore = function(config){
41129     
41130     
41131     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41132     
41133     
41134     this.addEvents({
41135         /**
41136          * @event initialize
41137          * Fires when the editor is fully initialized (including the iframe)
41138          * @param {Roo.HtmlEditorCore} this
41139          */
41140         initialize: true,
41141         /**
41142          * @event activate
41143          * Fires when the editor is first receives the focus. Any insertion must wait
41144          * until after this event.
41145          * @param {Roo.HtmlEditorCore} this
41146          */
41147         activate: true,
41148          /**
41149          * @event beforesync
41150          * Fires before the textarea is updated with content from the editor iframe. Return false
41151          * to cancel the sync.
41152          * @param {Roo.HtmlEditorCore} this
41153          * @param {String} html
41154          */
41155         beforesync: true,
41156          /**
41157          * @event beforepush
41158          * Fires before the iframe editor is updated with content from the textarea. Return false
41159          * to cancel the push.
41160          * @param {Roo.HtmlEditorCore} this
41161          * @param {String} html
41162          */
41163         beforepush: true,
41164          /**
41165          * @event sync
41166          * Fires when the textarea is updated with content from the editor iframe.
41167          * @param {Roo.HtmlEditorCore} this
41168          * @param {String} html
41169          */
41170         sync: true,
41171          /**
41172          * @event push
41173          * Fires when the iframe editor is updated with content from the textarea.
41174          * @param {Roo.HtmlEditorCore} this
41175          * @param {String} html
41176          */
41177         push: true,
41178         
41179         /**
41180          * @event editorevent
41181          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41182          * @param {Roo.HtmlEditorCore} this
41183          */
41184         editorevent: true
41185         
41186     });
41187     
41188     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41189     
41190     // defaults : white / black...
41191     this.applyBlacklists();
41192     
41193     
41194     
41195 };
41196
41197
41198 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41199
41200
41201      /**
41202      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41203      */
41204     
41205     owner : false,
41206     
41207      /**
41208      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41209      *                        Roo.resizable.
41210      */
41211     resizable : false,
41212      /**
41213      * @cfg {Number} height (in pixels)
41214      */   
41215     height: 300,
41216    /**
41217      * @cfg {Number} width (in pixels)
41218      */   
41219     width: 500,
41220     
41221     /**
41222      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41223      * 
41224      */
41225     stylesheets: false,
41226     
41227     // id of frame..
41228     frameId: false,
41229     
41230     // private properties
41231     validationEvent : false,
41232     deferHeight: true,
41233     initialized : false,
41234     activated : false,
41235     sourceEditMode : false,
41236     onFocus : Roo.emptyFn,
41237     iframePad:3,
41238     hideMode:'offsets',
41239     
41240     clearUp: true,
41241     
41242     // blacklist + whitelisted elements..
41243     black: false,
41244     white: false,
41245      
41246     
41247
41248     /**
41249      * Protected method that will not generally be called directly. It
41250      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41251      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41252      */
41253     getDocMarkup : function(){
41254         // body styles..
41255         var st = '';
41256         
41257         // inherit styels from page...?? 
41258         if (this.stylesheets === false) {
41259             
41260             Roo.get(document.head).select('style').each(function(node) {
41261                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41262             });
41263             
41264             Roo.get(document.head).select('link').each(function(node) { 
41265                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41266             });
41267             
41268         } else if (!this.stylesheets.length) {
41269                 // simple..
41270                 st = '<style type="text/css">' +
41271                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41272                    '</style>';
41273         } else { 
41274             
41275         }
41276         
41277         st +=  '<style type="text/css">' +
41278             'IMG { cursor: pointer } ' +
41279         '</style>';
41280
41281         
41282         return '<html><head>' + st  +
41283             //<style type="text/css">' +
41284             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41285             //'</style>' +
41286             ' </head><body class="roo-htmleditor-body"></body></html>';
41287     },
41288
41289     // private
41290     onRender : function(ct, position)
41291     {
41292         var _t = this;
41293         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41294         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41295         
41296         
41297         this.el.dom.style.border = '0 none';
41298         this.el.dom.setAttribute('tabIndex', -1);
41299         this.el.addClass('x-hidden hide');
41300         
41301         
41302         
41303         if(Roo.isIE){ // fix IE 1px bogus margin
41304             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41305         }
41306        
41307         
41308         this.frameId = Roo.id();
41309         
41310          
41311         
41312         var iframe = this.owner.wrap.createChild({
41313             tag: 'iframe',
41314             cls: 'form-control', // bootstrap..
41315             id: this.frameId,
41316             name: this.frameId,
41317             frameBorder : 'no',
41318             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41319         }, this.el
41320         );
41321         
41322         
41323         this.iframe = iframe.dom;
41324
41325          this.assignDocWin();
41326         
41327         this.doc.designMode = 'on';
41328        
41329         this.doc.open();
41330         this.doc.write(this.getDocMarkup());
41331         this.doc.close();
41332
41333         
41334         var task = { // must defer to wait for browser to be ready
41335             run : function(){
41336                 //console.log("run task?" + this.doc.readyState);
41337                 this.assignDocWin();
41338                 if(this.doc.body || this.doc.readyState == 'complete'){
41339                     try {
41340                         this.doc.designMode="on";
41341                     } catch (e) {
41342                         return;
41343                     }
41344                     Roo.TaskMgr.stop(task);
41345                     this.initEditor.defer(10, this);
41346                 }
41347             },
41348             interval : 10,
41349             duration: 10000,
41350             scope: this
41351         };
41352         Roo.TaskMgr.start(task);
41353
41354     },
41355
41356     // private
41357     onResize : function(w, h)
41358     {
41359          Roo.log('resize: ' +w + ',' + h );
41360         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41361         if(!this.iframe){
41362             return;
41363         }
41364         if(typeof w == 'number'){
41365             
41366             this.iframe.style.width = w + 'px';
41367         }
41368         if(typeof h == 'number'){
41369             
41370             this.iframe.style.height = h + 'px';
41371             if(this.doc){
41372                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41373             }
41374         }
41375         
41376     },
41377
41378     /**
41379      * Toggles the editor between standard and source edit mode.
41380      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41381      */
41382     toggleSourceEdit : function(sourceEditMode){
41383         
41384         this.sourceEditMode = sourceEditMode === true;
41385         
41386         if(this.sourceEditMode){
41387  
41388             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41389             
41390         }else{
41391             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41392             //this.iframe.className = '';
41393             this.deferFocus();
41394         }
41395         //this.setSize(this.owner.wrap.getSize());
41396         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41397     },
41398
41399     
41400   
41401
41402     /**
41403      * Protected method that will not generally be called directly. If you need/want
41404      * custom HTML cleanup, this is the method you should override.
41405      * @param {String} html The HTML to be cleaned
41406      * return {String} The cleaned HTML
41407      */
41408     cleanHtml : function(html){
41409         html = String(html);
41410         if(html.length > 5){
41411             if(Roo.isSafari){ // strip safari nonsense
41412                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41413             }
41414         }
41415         if(html == '&nbsp;'){
41416             html = '';
41417         }
41418         return html;
41419     },
41420
41421     /**
41422      * HTML Editor -> Textarea
41423      * Protected method that will not generally be called directly. Syncs the contents
41424      * of the editor iframe with the textarea.
41425      */
41426     syncValue : function(){
41427         if(this.initialized){
41428             var bd = (this.doc.body || this.doc.documentElement);
41429             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41430             var html = bd.innerHTML;
41431             if(Roo.isSafari){
41432                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41433                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41434                 if(m && m[1]){
41435                     html = '<div style="'+m[0]+'">' + html + '</div>';
41436                 }
41437             }
41438             html = this.cleanHtml(html);
41439             // fix up the special chars.. normaly like back quotes in word...
41440             // however we do not want to do this with chinese..
41441             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41442                 var cc = b.charCodeAt();
41443                 if (
41444                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41445                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41446                     (cc >= 0xf900 && cc < 0xfb00 )
41447                 ) {
41448                         return b;
41449                 }
41450                 return "&#"+cc+";" 
41451             });
41452             if(this.owner.fireEvent('beforesync', this, html) !== false){
41453                 this.el.dom.value = html;
41454                 this.owner.fireEvent('sync', this, html);
41455             }
41456         }
41457     },
41458
41459     /**
41460      * Protected method that will not generally be called directly. Pushes the value of the textarea
41461      * into the iframe editor.
41462      */
41463     pushValue : function(){
41464         if(this.initialized){
41465             var v = this.el.dom.value.trim();
41466             
41467 //            if(v.length < 1){
41468 //                v = '&#160;';
41469 //            }
41470             
41471             if(this.owner.fireEvent('beforepush', this, v) !== false){
41472                 var d = (this.doc.body || this.doc.documentElement);
41473                 d.innerHTML = v;
41474                 this.cleanUpPaste();
41475                 this.el.dom.value = d.innerHTML;
41476                 this.owner.fireEvent('push', this, v);
41477             }
41478         }
41479     },
41480
41481     // private
41482     deferFocus : function(){
41483         this.focus.defer(10, this);
41484     },
41485
41486     // doc'ed in Field
41487     focus : function(){
41488         if(this.win && !this.sourceEditMode){
41489             this.win.focus();
41490         }else{
41491             this.el.focus();
41492         }
41493     },
41494     
41495     assignDocWin: function()
41496     {
41497         var iframe = this.iframe;
41498         
41499          if(Roo.isIE){
41500             this.doc = iframe.contentWindow.document;
41501             this.win = iframe.contentWindow;
41502         } else {
41503 //            if (!Roo.get(this.frameId)) {
41504 //                return;
41505 //            }
41506 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41507 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41508             
41509             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41510                 return;
41511             }
41512             
41513             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41514             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41515         }
41516     },
41517     
41518     // private
41519     initEditor : function(){
41520         //console.log("INIT EDITOR");
41521         this.assignDocWin();
41522         
41523         
41524         
41525         this.doc.designMode="on";
41526         this.doc.open();
41527         this.doc.write(this.getDocMarkup());
41528         this.doc.close();
41529         
41530         var dbody = (this.doc.body || this.doc.documentElement);
41531         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41532         // this copies styles from the containing element into thsi one..
41533         // not sure why we need all of this..
41534         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41535         
41536         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41537         //ss['background-attachment'] = 'fixed'; // w3c
41538         dbody.bgProperties = 'fixed'; // ie
41539         //Roo.DomHelper.applyStyles(dbody, ss);
41540         Roo.EventManager.on(this.doc, {
41541             //'mousedown': this.onEditorEvent,
41542             'mouseup': this.onEditorEvent,
41543             'dblclick': this.onEditorEvent,
41544             'click': this.onEditorEvent,
41545             'keyup': this.onEditorEvent,
41546             buffer:100,
41547             scope: this
41548         });
41549         if(Roo.isGecko){
41550             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41551         }
41552         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41553             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41554         }
41555         this.initialized = true;
41556
41557         this.owner.fireEvent('initialize', this);
41558         this.pushValue();
41559     },
41560
41561     // private
41562     onDestroy : function(){
41563         
41564         
41565         
41566         if(this.rendered){
41567             
41568             //for (var i =0; i < this.toolbars.length;i++) {
41569             //    // fixme - ask toolbars for heights?
41570             //    this.toolbars[i].onDestroy();
41571            // }
41572             
41573             //this.wrap.dom.innerHTML = '';
41574             //this.wrap.remove();
41575         }
41576     },
41577
41578     // private
41579     onFirstFocus : function(){
41580         
41581         this.assignDocWin();
41582         
41583         
41584         this.activated = true;
41585          
41586     
41587         if(Roo.isGecko){ // prevent silly gecko errors
41588             this.win.focus();
41589             var s = this.win.getSelection();
41590             if(!s.focusNode || s.focusNode.nodeType != 3){
41591                 var r = s.getRangeAt(0);
41592                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41593                 r.collapse(true);
41594                 this.deferFocus();
41595             }
41596             try{
41597                 this.execCmd('useCSS', true);
41598                 this.execCmd('styleWithCSS', false);
41599             }catch(e){}
41600         }
41601         this.owner.fireEvent('activate', this);
41602     },
41603
41604     // private
41605     adjustFont: function(btn){
41606         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41607         //if(Roo.isSafari){ // safari
41608         //    adjust *= 2;
41609        // }
41610         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41611         if(Roo.isSafari){ // safari
41612             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41613             v =  (v < 10) ? 10 : v;
41614             v =  (v > 48) ? 48 : v;
41615             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41616             
41617         }
41618         
41619         
41620         v = Math.max(1, v+adjust);
41621         
41622         this.execCmd('FontSize', v  );
41623     },
41624
41625     onEditorEvent : function(e)
41626     {
41627         this.owner.fireEvent('editorevent', this, e);
41628       //  this.updateToolbar();
41629         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41630     },
41631
41632     insertTag : function(tg)
41633     {
41634         // could be a bit smarter... -> wrap the current selected tRoo..
41635         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41636             
41637             range = this.createRange(this.getSelection());
41638             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41639             wrappingNode.appendChild(range.extractContents());
41640             range.insertNode(wrappingNode);
41641
41642             return;
41643             
41644             
41645             
41646         }
41647         this.execCmd("formatblock",   tg);
41648         
41649     },
41650     
41651     insertText : function(txt)
41652     {
41653         
41654         
41655         var range = this.createRange();
41656         range.deleteContents();
41657                //alert(Sender.getAttribute('label'));
41658                
41659         range.insertNode(this.doc.createTextNode(txt));
41660     } ,
41661     
41662      
41663
41664     /**
41665      * Executes a Midas editor command on the editor document and performs necessary focus and
41666      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41667      * @param {String} cmd The Midas command
41668      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41669      */
41670     relayCmd : function(cmd, value){
41671         this.win.focus();
41672         this.execCmd(cmd, value);
41673         this.owner.fireEvent('editorevent', this);
41674         //this.updateToolbar();
41675         this.owner.deferFocus();
41676     },
41677
41678     /**
41679      * Executes a Midas editor command directly on the editor document.
41680      * For visual commands, you should use {@link #relayCmd} instead.
41681      * <b>This should only be called after the editor is initialized.</b>
41682      * @param {String} cmd The Midas command
41683      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41684      */
41685     execCmd : function(cmd, value){
41686         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41687         this.syncValue();
41688     },
41689  
41690  
41691    
41692     /**
41693      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41694      * to insert tRoo.
41695      * @param {String} text | dom node.. 
41696      */
41697     insertAtCursor : function(text)
41698     {
41699         
41700         
41701         
41702         if(!this.activated){
41703             return;
41704         }
41705         /*
41706         if(Roo.isIE){
41707             this.win.focus();
41708             var r = this.doc.selection.createRange();
41709             if(r){
41710                 r.collapse(true);
41711                 r.pasteHTML(text);
41712                 this.syncValue();
41713                 this.deferFocus();
41714             
41715             }
41716             return;
41717         }
41718         */
41719         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41720             this.win.focus();
41721             
41722             
41723             // from jquery ui (MIT licenced)
41724             var range, node;
41725             var win = this.win;
41726             
41727             if (win.getSelection && win.getSelection().getRangeAt) {
41728                 range = win.getSelection().getRangeAt(0);
41729                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41730                 range.insertNode(node);
41731             } else if (win.document.selection && win.document.selection.createRange) {
41732                 // no firefox support
41733                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41734                 win.document.selection.createRange().pasteHTML(txt);
41735             } else {
41736                 // no firefox support
41737                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41738                 this.execCmd('InsertHTML', txt);
41739             } 
41740             
41741             this.syncValue();
41742             
41743             this.deferFocus();
41744         }
41745     },
41746  // private
41747     mozKeyPress : function(e){
41748         if(e.ctrlKey){
41749             var c = e.getCharCode(), cmd;
41750           
41751             if(c > 0){
41752                 c = String.fromCharCode(c).toLowerCase();
41753                 switch(c){
41754                     case 'b':
41755                         cmd = 'bold';
41756                         break;
41757                     case 'i':
41758                         cmd = 'italic';
41759                         break;
41760                     
41761                     case 'u':
41762                         cmd = 'underline';
41763                         break;
41764                     
41765                     case 'v':
41766                         this.cleanUpPaste.defer(100, this);
41767                         return;
41768                         
41769                 }
41770                 if(cmd){
41771                     this.win.focus();
41772                     this.execCmd(cmd);
41773                     this.deferFocus();
41774                     e.preventDefault();
41775                 }
41776                 
41777             }
41778         }
41779     },
41780
41781     // private
41782     fixKeys : function(){ // load time branching for fastest keydown performance
41783         if(Roo.isIE){
41784             return function(e){
41785                 var k = e.getKey(), r;
41786                 if(k == e.TAB){
41787                     e.stopEvent();
41788                     r = this.doc.selection.createRange();
41789                     if(r){
41790                         r.collapse(true);
41791                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41792                         this.deferFocus();
41793                     }
41794                     return;
41795                 }
41796                 
41797                 if(k == e.ENTER){
41798                     r = this.doc.selection.createRange();
41799                     if(r){
41800                         var target = r.parentElement();
41801                         if(!target || target.tagName.toLowerCase() != 'li'){
41802                             e.stopEvent();
41803                             r.pasteHTML('<br />');
41804                             r.collapse(false);
41805                             r.select();
41806                         }
41807                     }
41808                 }
41809                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41810                     this.cleanUpPaste.defer(100, this);
41811                     return;
41812                 }
41813                 
41814                 
41815             };
41816         }else if(Roo.isOpera){
41817             return function(e){
41818                 var k = e.getKey();
41819                 if(k == e.TAB){
41820                     e.stopEvent();
41821                     this.win.focus();
41822                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41823                     this.deferFocus();
41824                 }
41825                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41826                     this.cleanUpPaste.defer(100, this);
41827                     return;
41828                 }
41829                 
41830             };
41831         }else if(Roo.isSafari){
41832             return function(e){
41833                 var k = e.getKey();
41834                 
41835                 if(k == e.TAB){
41836                     e.stopEvent();
41837                     this.execCmd('InsertText','\t');
41838                     this.deferFocus();
41839                     return;
41840                 }
41841                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41842                     this.cleanUpPaste.defer(100, this);
41843                     return;
41844                 }
41845                 
41846              };
41847         }
41848     }(),
41849     
41850     getAllAncestors: function()
41851     {
41852         var p = this.getSelectedNode();
41853         var a = [];
41854         if (!p) {
41855             a.push(p); // push blank onto stack..
41856             p = this.getParentElement();
41857         }
41858         
41859         
41860         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41861             a.push(p);
41862             p = p.parentNode;
41863         }
41864         a.push(this.doc.body);
41865         return a;
41866     },
41867     lastSel : false,
41868     lastSelNode : false,
41869     
41870     
41871     getSelection : function() 
41872     {
41873         this.assignDocWin();
41874         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41875     },
41876     
41877     getSelectedNode: function() 
41878     {
41879         // this may only work on Gecko!!!
41880         
41881         // should we cache this!!!!
41882         
41883         
41884         
41885          
41886         var range = this.createRange(this.getSelection()).cloneRange();
41887         
41888         if (Roo.isIE) {
41889             var parent = range.parentElement();
41890             while (true) {
41891                 var testRange = range.duplicate();
41892                 testRange.moveToElementText(parent);
41893                 if (testRange.inRange(range)) {
41894                     break;
41895                 }
41896                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41897                     break;
41898                 }
41899                 parent = parent.parentElement;
41900             }
41901             return parent;
41902         }
41903         
41904         // is ancestor a text element.
41905         var ac =  range.commonAncestorContainer;
41906         if (ac.nodeType == 3) {
41907             ac = ac.parentNode;
41908         }
41909         
41910         var ar = ac.childNodes;
41911          
41912         var nodes = [];
41913         var other_nodes = [];
41914         var has_other_nodes = false;
41915         for (var i=0;i<ar.length;i++) {
41916             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41917                 continue;
41918             }
41919             // fullly contained node.
41920             
41921             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41922                 nodes.push(ar[i]);
41923                 continue;
41924             }
41925             
41926             // probably selected..
41927             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41928                 other_nodes.push(ar[i]);
41929                 continue;
41930             }
41931             // outer..
41932             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41933                 continue;
41934             }
41935             
41936             
41937             has_other_nodes = true;
41938         }
41939         if (!nodes.length && other_nodes.length) {
41940             nodes= other_nodes;
41941         }
41942         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41943             return false;
41944         }
41945         
41946         return nodes[0];
41947     },
41948     createRange: function(sel)
41949     {
41950         // this has strange effects when using with 
41951         // top toolbar - not sure if it's a great idea.
41952         //this.editor.contentWindow.focus();
41953         if (typeof sel != "undefined") {
41954             try {
41955                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41956             } catch(e) {
41957                 return this.doc.createRange();
41958             }
41959         } else {
41960             return this.doc.createRange();
41961         }
41962     },
41963     getParentElement: function()
41964     {
41965         
41966         this.assignDocWin();
41967         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41968         
41969         var range = this.createRange(sel);
41970          
41971         try {
41972             var p = range.commonAncestorContainer;
41973             while (p.nodeType == 3) { // text node
41974                 p = p.parentNode;
41975             }
41976             return p;
41977         } catch (e) {
41978             return null;
41979         }
41980     
41981     },
41982     /***
41983      *
41984      * Range intersection.. the hard stuff...
41985      *  '-1' = before
41986      *  '0' = hits..
41987      *  '1' = after.
41988      *         [ -- selected range --- ]
41989      *   [fail]                        [fail]
41990      *
41991      *    basically..
41992      *      if end is before start or  hits it. fail.
41993      *      if start is after end or hits it fail.
41994      *
41995      *   if either hits (but other is outside. - then it's not 
41996      *   
41997      *    
41998      **/
41999     
42000     
42001     // @see http://www.thismuchiknow.co.uk/?p=64.
42002     rangeIntersectsNode : function(range, node)
42003     {
42004         var nodeRange = node.ownerDocument.createRange();
42005         try {
42006             nodeRange.selectNode(node);
42007         } catch (e) {
42008             nodeRange.selectNodeContents(node);
42009         }
42010     
42011         var rangeStartRange = range.cloneRange();
42012         rangeStartRange.collapse(true);
42013     
42014         var rangeEndRange = range.cloneRange();
42015         rangeEndRange.collapse(false);
42016     
42017         var nodeStartRange = nodeRange.cloneRange();
42018         nodeStartRange.collapse(true);
42019     
42020         var nodeEndRange = nodeRange.cloneRange();
42021         nodeEndRange.collapse(false);
42022     
42023         return rangeStartRange.compareBoundaryPoints(
42024                  Range.START_TO_START, nodeEndRange) == -1 &&
42025                rangeEndRange.compareBoundaryPoints(
42026                  Range.START_TO_START, nodeStartRange) == 1;
42027         
42028          
42029     },
42030     rangeCompareNode : function(range, node)
42031     {
42032         var nodeRange = node.ownerDocument.createRange();
42033         try {
42034             nodeRange.selectNode(node);
42035         } catch (e) {
42036             nodeRange.selectNodeContents(node);
42037         }
42038         
42039         
42040         range.collapse(true);
42041     
42042         nodeRange.collapse(true);
42043      
42044         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42045         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42046          
42047         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42048         
42049         var nodeIsBefore   =  ss == 1;
42050         var nodeIsAfter    = ee == -1;
42051         
42052         if (nodeIsBefore && nodeIsAfter)
42053             return 0; // outer
42054         if (!nodeIsBefore && nodeIsAfter)
42055             return 1; //right trailed.
42056         
42057         if (nodeIsBefore && !nodeIsAfter)
42058             return 2;  // left trailed.
42059         // fully contined.
42060         return 3;
42061     },
42062
42063     // private? - in a new class?
42064     cleanUpPaste :  function()
42065     {
42066         // cleans up the whole document..
42067         Roo.log('cleanuppaste');
42068         
42069         this.cleanUpChildren(this.doc.body);
42070         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42071         if (clean != this.doc.body.innerHTML) {
42072             this.doc.body.innerHTML = clean;
42073         }
42074         
42075     },
42076     
42077     cleanWordChars : function(input) {// change the chars to hex code
42078         var he = Roo.HtmlEditorCore;
42079         
42080         var output = input;
42081         Roo.each(he.swapCodes, function(sw) { 
42082             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42083             
42084             output = output.replace(swapper, sw[1]);
42085         });
42086         
42087         return output;
42088     },
42089     
42090     
42091     cleanUpChildren : function (n)
42092     {
42093         if (!n.childNodes.length) {
42094             return;
42095         }
42096         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42097            this.cleanUpChild(n.childNodes[i]);
42098         }
42099     },
42100     
42101     
42102         
42103     
42104     cleanUpChild : function (node)
42105     {
42106         var ed = this;
42107         //console.log(node);
42108         if (node.nodeName == "#text") {
42109             // clean up silly Windows -- stuff?
42110             return; 
42111         }
42112         if (node.nodeName == "#comment") {
42113             node.parentNode.removeChild(node);
42114             // clean up silly Windows -- stuff?
42115             return; 
42116         }
42117         var lcname = node.tagName.toLowerCase();
42118         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42119         // whitelist of tags..
42120         
42121         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42122             // remove node.
42123             node.parentNode.removeChild(node);
42124             return;
42125             
42126         }
42127         
42128         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42129         
42130         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42131         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42132         
42133         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42134         //    remove_keep_children = true;
42135         //}
42136         
42137         if (remove_keep_children) {
42138             this.cleanUpChildren(node);
42139             // inserts everything just before this node...
42140             while (node.childNodes.length) {
42141                 var cn = node.childNodes[0];
42142                 node.removeChild(cn);
42143                 node.parentNode.insertBefore(cn, node);
42144             }
42145             node.parentNode.removeChild(node);
42146             return;
42147         }
42148         
42149         if (!node.attributes || !node.attributes.length) {
42150             this.cleanUpChildren(node);
42151             return;
42152         }
42153         
42154         function cleanAttr(n,v)
42155         {
42156             
42157             if (v.match(/^\./) || v.match(/^\//)) {
42158                 return;
42159             }
42160             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42161                 return;
42162             }
42163             if (v.match(/^#/)) {
42164                 return;
42165             }
42166 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42167             node.removeAttribute(n);
42168             
42169         }
42170         
42171         var cwhite = this.cwhite;
42172         var cblack = this.cblack;
42173             
42174         function cleanStyle(n,v)
42175         {
42176             if (v.match(/expression/)) { //XSS?? should we even bother..
42177                 node.removeAttribute(n);
42178                 return;
42179             }
42180             
42181             var parts = v.split(/;/);
42182             var clean = [];
42183             
42184             Roo.each(parts, function(p) {
42185                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42186                 if (!p.length) {
42187                     return true;
42188                 }
42189                 var l = p.split(':').shift().replace(/\s+/g,'');
42190                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42191                 
42192                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42193 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42194                     //node.removeAttribute(n);
42195                     return true;
42196                 }
42197                 //Roo.log()
42198                 // only allow 'c whitelisted system attributes'
42199                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42200 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42201                     //node.removeAttribute(n);
42202                     return true;
42203                 }
42204                 
42205                 
42206                  
42207                 
42208                 clean.push(p);
42209                 return true;
42210             });
42211             if (clean.length) { 
42212                 node.setAttribute(n, clean.join(';'));
42213             } else {
42214                 node.removeAttribute(n);
42215             }
42216             
42217         }
42218         
42219         
42220         for (var i = node.attributes.length-1; i > -1 ; i--) {
42221             var a = node.attributes[i];
42222             //console.log(a);
42223             
42224             if (a.name.toLowerCase().substr(0,2)=='on')  {
42225                 node.removeAttribute(a.name);
42226                 continue;
42227             }
42228             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42229                 node.removeAttribute(a.name);
42230                 continue;
42231             }
42232             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42233                 cleanAttr(a.name,a.value); // fixme..
42234                 continue;
42235             }
42236             if (a.name == 'style') {
42237                 cleanStyle(a.name,a.value);
42238                 continue;
42239             }
42240             /// clean up MS crap..
42241             // tecnically this should be a list of valid class'es..
42242             
42243             
42244             if (a.name == 'class') {
42245                 if (a.value.match(/^Mso/)) {
42246                     node.className = '';
42247                 }
42248                 
42249                 if (a.value.match(/body/)) {
42250                     node.className = '';
42251                 }
42252                 continue;
42253             }
42254             
42255             // style cleanup!?
42256             // class cleanup?
42257             
42258         }
42259         
42260         
42261         this.cleanUpChildren(node);
42262         
42263         
42264     },
42265     
42266     /**
42267      * Clean up MS wordisms...
42268      */
42269     cleanWord : function(node)
42270     {
42271         
42272         
42273         if (!node) {
42274             this.cleanWord(this.doc.body);
42275             return;
42276         }
42277         if (node.nodeName == "#text") {
42278             // clean up silly Windows -- stuff?
42279             return; 
42280         }
42281         if (node.nodeName == "#comment") {
42282             node.parentNode.removeChild(node);
42283             // clean up silly Windows -- stuff?
42284             return; 
42285         }
42286         
42287         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42288             node.parentNode.removeChild(node);
42289             return;
42290         }
42291         
42292         // remove - but keep children..
42293         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42294             while (node.childNodes.length) {
42295                 var cn = node.childNodes[0];
42296                 node.removeChild(cn);
42297                 node.parentNode.insertBefore(cn, node);
42298             }
42299             node.parentNode.removeChild(node);
42300             this.iterateChildren(node, this.cleanWord);
42301             return;
42302         }
42303         // clean styles
42304         if (node.className.length) {
42305             
42306             var cn = node.className.split(/\W+/);
42307             var cna = [];
42308             Roo.each(cn, function(cls) {
42309                 if (cls.match(/Mso[a-zA-Z]+/)) {
42310                     return;
42311                 }
42312                 cna.push(cls);
42313             });
42314             node.className = cna.length ? cna.join(' ') : '';
42315             if (!cna.length) {
42316                 node.removeAttribute("class");
42317             }
42318         }
42319         
42320         if (node.hasAttribute("lang")) {
42321             node.removeAttribute("lang");
42322         }
42323         
42324         if (node.hasAttribute("style")) {
42325             
42326             var styles = node.getAttribute("style").split(";");
42327             var nstyle = [];
42328             Roo.each(styles, function(s) {
42329                 if (!s.match(/:/)) {
42330                     return;
42331                 }
42332                 var kv = s.split(":");
42333                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42334                     return;
42335                 }
42336                 // what ever is left... we allow.
42337                 nstyle.push(s);
42338             });
42339             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42340             if (!nstyle.length) {
42341                 node.removeAttribute('style');
42342             }
42343         }
42344         this.iterateChildren(node, this.cleanWord);
42345         
42346         
42347         
42348     },
42349     /**
42350      * iterateChildren of a Node, calling fn each time, using this as the scole..
42351      * @param {DomNode} node node to iterate children of.
42352      * @param {Function} fn method of this class to call on each item.
42353      */
42354     iterateChildren : function(node, fn)
42355     {
42356         if (!node.childNodes.length) {
42357                 return;
42358         }
42359         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42360            fn.call(this, node.childNodes[i])
42361         }
42362     },
42363     
42364     
42365     /**
42366      * cleanTableWidths.
42367      *
42368      * Quite often pasting from word etc.. results in tables with column and widths.
42369      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42370      *
42371      */
42372     cleanTableWidths : function(node)
42373     {
42374          
42375          
42376         if (!node) {
42377             this.cleanTableWidths(this.doc.body);
42378             return;
42379         }
42380         
42381         // ignore list...
42382         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42383             return; 
42384         }
42385         Roo.log(node.tagName);
42386         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42387             this.iterateChildren(node, this.cleanTableWidths);
42388             return;
42389         }
42390         if (node.hasAttribute('width')) {
42391             node.removeAttribute('width');
42392         }
42393         
42394          
42395         if (node.hasAttribute("style")) {
42396             // pretty basic...
42397             
42398             var styles = node.getAttribute("style").split(";");
42399             var nstyle = [];
42400             Roo.each(styles, function(s) {
42401                 if (!s.match(/:/)) {
42402                     return;
42403                 }
42404                 var kv = s.split(":");
42405                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42406                     return;
42407                 }
42408                 // what ever is left... we allow.
42409                 nstyle.push(s);
42410             });
42411             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42412             if (!nstyle.length) {
42413                 node.removeAttribute('style');
42414             }
42415         }
42416         
42417         this.iterateChildren(node, this.cleanTableWidths);
42418         
42419         
42420     },
42421     
42422     
42423     
42424     
42425     domToHTML : function(currentElement, depth, nopadtext) {
42426         
42427         depth = depth || 0;
42428         nopadtext = nopadtext || false;
42429     
42430         if (!currentElement) {
42431             return this.domToHTML(this.doc.body);
42432         }
42433         
42434         //Roo.log(currentElement);
42435         var j;
42436         var allText = false;
42437         var nodeName = currentElement.nodeName;
42438         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42439         
42440         if  (nodeName == '#text') {
42441             
42442             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42443         }
42444         
42445         
42446         var ret = '';
42447         if (nodeName != 'BODY') {
42448              
42449             var i = 0;
42450             // Prints the node tagName, such as <A>, <IMG>, etc
42451             if (tagName) {
42452                 var attr = [];
42453                 for(i = 0; i < currentElement.attributes.length;i++) {
42454                     // quoting?
42455                     var aname = currentElement.attributes.item(i).name;
42456                     if (!currentElement.attributes.item(i).value.length) {
42457                         continue;
42458                     }
42459                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42460                 }
42461                 
42462                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42463             } 
42464             else {
42465                 
42466                 // eack
42467             }
42468         } else {
42469             tagName = false;
42470         }
42471         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42472             return ret;
42473         }
42474         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42475             nopadtext = true;
42476         }
42477         
42478         
42479         // Traverse the tree
42480         i = 0;
42481         var currentElementChild = currentElement.childNodes.item(i);
42482         var allText = true;
42483         var innerHTML  = '';
42484         lastnode = '';
42485         while (currentElementChild) {
42486             // Formatting code (indent the tree so it looks nice on the screen)
42487             var nopad = nopadtext;
42488             if (lastnode == 'SPAN') {
42489                 nopad  = true;
42490             }
42491             // text
42492             if  (currentElementChild.nodeName == '#text') {
42493                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42494                 toadd = nopadtext ? toadd : toadd.trim();
42495                 if (!nopad && toadd.length > 80) {
42496                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42497                 }
42498                 innerHTML  += toadd;
42499                 
42500                 i++;
42501                 currentElementChild = currentElement.childNodes.item(i);
42502                 lastNode = '';
42503                 continue;
42504             }
42505             allText = false;
42506             
42507             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42508                 
42509             // Recursively traverse the tree structure of the child node
42510             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42511             lastnode = currentElementChild.nodeName;
42512             i++;
42513             currentElementChild=currentElement.childNodes.item(i);
42514         }
42515         
42516         ret += innerHTML;
42517         
42518         if (!allText) {
42519                 // The remaining code is mostly for formatting the tree
42520             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42521         }
42522         
42523         
42524         if (tagName) {
42525             ret+= "</"+tagName+">";
42526         }
42527         return ret;
42528         
42529     },
42530         
42531     applyBlacklists : function()
42532     {
42533         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42534         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42535         
42536         this.white = [];
42537         this.black = [];
42538         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42539             if (b.indexOf(tag) > -1) {
42540                 return;
42541             }
42542             this.white.push(tag);
42543             
42544         }, this);
42545         
42546         Roo.each(w, function(tag) {
42547             if (b.indexOf(tag) > -1) {
42548                 return;
42549             }
42550             if (this.white.indexOf(tag) > -1) {
42551                 return;
42552             }
42553             this.white.push(tag);
42554             
42555         }, this);
42556         
42557         
42558         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42559             if (w.indexOf(tag) > -1) {
42560                 return;
42561             }
42562             this.black.push(tag);
42563             
42564         }, this);
42565         
42566         Roo.each(b, function(tag) {
42567             if (w.indexOf(tag) > -1) {
42568                 return;
42569             }
42570             if (this.black.indexOf(tag) > -1) {
42571                 return;
42572             }
42573             this.black.push(tag);
42574             
42575         }, this);
42576         
42577         
42578         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42579         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42580         
42581         this.cwhite = [];
42582         this.cblack = [];
42583         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42584             if (b.indexOf(tag) > -1) {
42585                 return;
42586             }
42587             this.cwhite.push(tag);
42588             
42589         }, this);
42590         
42591         Roo.each(w, function(tag) {
42592             if (b.indexOf(tag) > -1) {
42593                 return;
42594             }
42595             if (this.cwhite.indexOf(tag) > -1) {
42596                 return;
42597             }
42598             this.cwhite.push(tag);
42599             
42600         }, this);
42601         
42602         
42603         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42604             if (w.indexOf(tag) > -1) {
42605                 return;
42606             }
42607             this.cblack.push(tag);
42608             
42609         }, this);
42610         
42611         Roo.each(b, function(tag) {
42612             if (w.indexOf(tag) > -1) {
42613                 return;
42614             }
42615             if (this.cblack.indexOf(tag) > -1) {
42616                 return;
42617             }
42618             this.cblack.push(tag);
42619             
42620         }, this);
42621     },
42622     
42623     setStylesheets : function(stylesheets)
42624     {
42625         if(typeof(stylesheets) == 'string'){
42626             Roo.get(this.iframe.contentDocument.head).createChild({
42627                 tag : 'link',
42628                 rel : 'stylesheet',
42629                 type : 'text/css',
42630                 href : stylesheets
42631             });
42632             
42633             return;
42634         }
42635         var _this = this;
42636      
42637         Roo.each(stylesheets, function(s) {
42638             if(!s.length){
42639                 return;
42640             }
42641             
42642             Roo.get(_this.iframe.contentDocument.head).createChild({
42643                 tag : 'link',
42644                 rel : 'stylesheet',
42645                 type : 'text/css',
42646                 href : s
42647             });
42648         });
42649
42650         
42651     },
42652     
42653     removeStylesheets : function()
42654     {
42655         var _this = this;
42656         
42657         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42658             s.remove();
42659         });
42660     }
42661     
42662     // hide stuff that is not compatible
42663     /**
42664      * @event blur
42665      * @hide
42666      */
42667     /**
42668      * @event change
42669      * @hide
42670      */
42671     /**
42672      * @event focus
42673      * @hide
42674      */
42675     /**
42676      * @event specialkey
42677      * @hide
42678      */
42679     /**
42680      * @cfg {String} fieldClass @hide
42681      */
42682     /**
42683      * @cfg {String} focusClass @hide
42684      */
42685     /**
42686      * @cfg {String} autoCreate @hide
42687      */
42688     /**
42689      * @cfg {String} inputType @hide
42690      */
42691     /**
42692      * @cfg {String} invalidClass @hide
42693      */
42694     /**
42695      * @cfg {String} invalidText @hide
42696      */
42697     /**
42698      * @cfg {String} msgFx @hide
42699      */
42700     /**
42701      * @cfg {String} validateOnBlur @hide
42702      */
42703 });
42704
42705 Roo.HtmlEditorCore.white = [
42706         'area', 'br', 'img', 'input', 'hr', 'wbr',
42707         
42708        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42709        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42710        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42711        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42712        'table',   'ul',         'xmp', 
42713        
42714        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42715       'thead',   'tr', 
42716      
42717       'dir', 'menu', 'ol', 'ul', 'dl',
42718        
42719       'embed',  'object'
42720 ];
42721
42722
42723 Roo.HtmlEditorCore.black = [
42724     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42725         'applet', // 
42726         'base',   'basefont', 'bgsound', 'blink',  'body', 
42727         'frame',  'frameset', 'head',    'html',   'ilayer', 
42728         'iframe', 'layer',  'link',     'meta',    'object',   
42729         'script', 'style' ,'title',  'xml' // clean later..
42730 ];
42731 Roo.HtmlEditorCore.clean = [
42732     'script', 'style', 'title', 'xml'
42733 ];
42734 Roo.HtmlEditorCore.remove = [
42735     'font'
42736 ];
42737 // attributes..
42738
42739 Roo.HtmlEditorCore.ablack = [
42740     'on'
42741 ];
42742     
42743 Roo.HtmlEditorCore.aclean = [ 
42744     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42745 ];
42746
42747 // protocols..
42748 Roo.HtmlEditorCore.pwhite= [
42749         'http',  'https',  'mailto'
42750 ];
42751
42752 // white listed style attributes.
42753 Roo.HtmlEditorCore.cwhite= [
42754       //  'text-align', /// default is to allow most things..
42755       
42756          
42757 //        'font-size'//??
42758 ];
42759
42760 // black listed style attributes.
42761 Roo.HtmlEditorCore.cblack= [
42762       //  'font-size' -- this can be set by the project 
42763 ];
42764
42765
42766 Roo.HtmlEditorCore.swapCodes   =[ 
42767     [    8211, "--" ], 
42768     [    8212, "--" ], 
42769     [    8216,  "'" ],  
42770     [    8217, "'" ],  
42771     [    8220, '"' ],  
42772     [    8221, '"' ],  
42773     [    8226, "*" ],  
42774     [    8230, "..." ]
42775 ]; 
42776
42777     //<script type="text/javascript">
42778
42779 /*
42780  * Ext JS Library 1.1.1
42781  * Copyright(c) 2006-2007, Ext JS, LLC.
42782  * Licence LGPL
42783  * 
42784  */
42785  
42786  
42787 Roo.form.HtmlEditor = function(config){
42788     
42789     
42790     
42791     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42792     
42793     if (!this.toolbars) {
42794         this.toolbars = [];
42795     }
42796     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42797     
42798     
42799 };
42800
42801 /**
42802  * @class Roo.form.HtmlEditor
42803  * @extends Roo.form.Field
42804  * Provides a lightweight HTML Editor component.
42805  *
42806  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42807  * 
42808  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42809  * supported by this editor.</b><br/><br/>
42810  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42811  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42812  */
42813 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42814     /**
42815      * @cfg {Boolean} clearUp
42816      */
42817     clearUp : true,
42818       /**
42819      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42820      */
42821     toolbars : false,
42822    
42823      /**
42824      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42825      *                        Roo.resizable.
42826      */
42827     resizable : false,
42828      /**
42829      * @cfg {Number} height (in pixels)
42830      */   
42831     height: 300,
42832    /**
42833      * @cfg {Number} width (in pixels)
42834      */   
42835     width: 500,
42836     
42837     /**
42838      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42839      * 
42840      */
42841     stylesheets: false,
42842     
42843     
42844      /**
42845      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42846      * 
42847      */
42848     cblack: false,
42849     /**
42850      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42851      * 
42852      */
42853     cwhite: false,
42854     
42855      /**
42856      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42857      * 
42858      */
42859     black: false,
42860     /**
42861      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42862      * 
42863      */
42864     white: false,
42865     
42866     // id of frame..
42867     frameId: false,
42868     
42869     // private properties
42870     validationEvent : false,
42871     deferHeight: true,
42872     initialized : false,
42873     activated : false,
42874     
42875     onFocus : Roo.emptyFn,
42876     iframePad:3,
42877     hideMode:'offsets',
42878     
42879     actionMode : 'container', // defaults to hiding it...
42880     
42881     defaultAutoCreate : { // modified by initCompnoent..
42882         tag: "textarea",
42883         style:"width:500px;height:300px;",
42884         autocomplete: "new-password"
42885     },
42886
42887     // private
42888     initComponent : function(){
42889         this.addEvents({
42890             /**
42891              * @event initialize
42892              * Fires when the editor is fully initialized (including the iframe)
42893              * @param {HtmlEditor} this
42894              */
42895             initialize: true,
42896             /**
42897              * @event activate
42898              * Fires when the editor is first receives the focus. Any insertion must wait
42899              * until after this event.
42900              * @param {HtmlEditor} this
42901              */
42902             activate: true,
42903              /**
42904              * @event beforesync
42905              * Fires before the textarea is updated with content from the editor iframe. Return false
42906              * to cancel the sync.
42907              * @param {HtmlEditor} this
42908              * @param {String} html
42909              */
42910             beforesync: true,
42911              /**
42912              * @event beforepush
42913              * Fires before the iframe editor is updated with content from the textarea. Return false
42914              * to cancel the push.
42915              * @param {HtmlEditor} this
42916              * @param {String} html
42917              */
42918             beforepush: true,
42919              /**
42920              * @event sync
42921              * Fires when the textarea is updated with content from the editor iframe.
42922              * @param {HtmlEditor} this
42923              * @param {String} html
42924              */
42925             sync: true,
42926              /**
42927              * @event push
42928              * Fires when the iframe editor is updated with content from the textarea.
42929              * @param {HtmlEditor} this
42930              * @param {String} html
42931              */
42932             push: true,
42933              /**
42934              * @event editmodechange
42935              * Fires when the editor switches edit modes
42936              * @param {HtmlEditor} this
42937              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42938              */
42939             editmodechange: true,
42940             /**
42941              * @event editorevent
42942              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42943              * @param {HtmlEditor} this
42944              */
42945             editorevent: true,
42946             /**
42947              * @event firstfocus
42948              * Fires when on first focus - needed by toolbars..
42949              * @param {HtmlEditor} this
42950              */
42951             firstfocus: true,
42952             /**
42953              * @event autosave
42954              * Auto save the htmlEditor value as a file into Events
42955              * @param {HtmlEditor} this
42956              */
42957             autosave: true,
42958             /**
42959              * @event savedpreview
42960              * preview the saved version of htmlEditor
42961              * @param {HtmlEditor} this
42962              */
42963             savedpreview: true,
42964             
42965             /**
42966             * @event stylesheetsclick
42967             * Fires when press the Sytlesheets button
42968             * @param {Roo.HtmlEditorCore} this
42969             */
42970             stylesheetsclick: true
42971         });
42972         this.defaultAutoCreate =  {
42973             tag: "textarea",
42974             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42975             autocomplete: "new-password"
42976         };
42977     },
42978
42979     /**
42980      * Protected method that will not generally be called directly. It
42981      * is called when the editor creates its toolbar. Override this method if you need to
42982      * add custom toolbar buttons.
42983      * @param {HtmlEditor} editor
42984      */
42985     createToolbar : function(editor){
42986         Roo.log("create toolbars");
42987         if (!editor.toolbars || !editor.toolbars.length) {
42988             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42989         }
42990         
42991         for (var i =0 ; i < editor.toolbars.length;i++) {
42992             editor.toolbars[i] = Roo.factory(
42993                     typeof(editor.toolbars[i]) == 'string' ?
42994                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42995                 Roo.form.HtmlEditor);
42996             editor.toolbars[i].init(editor);
42997         }
42998          
42999         
43000     },
43001
43002      
43003     // private
43004     onRender : function(ct, position)
43005     {
43006         var _t = this;
43007         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
43008         
43009         this.wrap = this.el.wrap({
43010             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43011         });
43012         
43013         this.editorcore.onRender(ct, position);
43014          
43015         if (this.resizable) {
43016             this.resizeEl = new Roo.Resizable(this.wrap, {
43017                 pinned : true,
43018                 wrap: true,
43019                 dynamic : true,
43020                 minHeight : this.height,
43021                 height: this.height,
43022                 handles : this.resizable,
43023                 width: this.width,
43024                 listeners : {
43025                     resize : function(r, w, h) {
43026                         _t.onResize(w,h); // -something
43027                     }
43028                 }
43029             });
43030             
43031         }
43032         this.createToolbar(this);
43033        
43034         
43035         if(!this.width){
43036             this.setSize(this.wrap.getSize());
43037         }
43038         if (this.resizeEl) {
43039             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43040             // should trigger onReize..
43041         }
43042         
43043         this.keyNav = new Roo.KeyNav(this.el, {
43044             
43045             "tab" : function(e){
43046                 e.preventDefault();
43047                 
43048                 var value = this.getValue();
43049                 
43050                 var start = this.el.dom.selectionStart;
43051                 var end = this.el.dom.selectionEnd;
43052                 
43053                 if(!e.shiftKey){
43054                     
43055                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43056                     this.el.dom.setSelectionRange(end + 1, end + 1);
43057                     return;
43058                 }
43059                 
43060                 var f = value.substring(0, start).split("\t");
43061                 
43062                 if(f.pop().length != 0){
43063                     return;
43064                 }
43065                 
43066                 this.setValue(f.join("\t") + value.substring(end));
43067                 this.el.dom.setSelectionRange(start - 1, start - 1);
43068                 
43069             },
43070             
43071             "home" : function(e){
43072                 e.preventDefault();
43073                 
43074                 var curr = this.el.dom.selectionStart;
43075                 var lines = this.getValue().split("\n");
43076                 
43077                 if(!lines.length){
43078                     return;
43079                 }
43080                 
43081                 if(e.ctrlKey){
43082                     this.el.dom.setSelectionRange(0, 0);
43083                     return;
43084                 }
43085                 
43086                 var pos = 0;
43087                 
43088                 for (var i = 0; i < lines.length;i++) {
43089                     pos += lines[i].length;
43090                     
43091                     if(i != 0){
43092                         pos += 1;
43093                     }
43094                     
43095                     if(pos < curr){
43096                         continue;
43097                     }
43098                     
43099                     pos -= lines[i].length;
43100                     
43101                     break;
43102                 }
43103                 
43104                 if(!e.shiftKey){
43105                     this.el.dom.setSelectionRange(pos, pos);
43106                     return;
43107                 }
43108                 
43109                 this.el.dom.selectionStart = pos;
43110                 this.el.dom.selectionEnd = curr;
43111             },
43112             
43113             "end" : function(e){
43114                 e.preventDefault();
43115                 
43116                 var curr = this.el.dom.selectionStart;
43117                 var lines = this.getValue().split("\n");
43118                 
43119                 if(!lines.length){
43120                     return;
43121                 }
43122                 
43123                 if(e.ctrlKey){
43124                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43125                     return;
43126                 }
43127                 
43128                 var pos = 0;
43129                 
43130                 for (var i = 0; i < lines.length;i++) {
43131                     
43132                     pos += lines[i].length;
43133                     
43134                     if(i != 0){
43135                         pos += 1;
43136                     }
43137                     
43138                     if(pos < curr){
43139                         continue;
43140                     }
43141                     
43142                     break;
43143                 }
43144                 
43145                 if(!e.shiftKey){
43146                     this.el.dom.setSelectionRange(pos, pos);
43147                     return;
43148                 }
43149                 
43150                 this.el.dom.selectionStart = curr;
43151                 this.el.dom.selectionEnd = pos;
43152             },
43153
43154             scope : this,
43155
43156             doRelay : function(foo, bar, hname){
43157                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43158             },
43159
43160             forceKeyDown: true
43161         });
43162         
43163 //        if(this.autosave && this.w){
43164 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43165 //        }
43166     },
43167
43168     // private
43169     onResize : function(w, h)
43170     {
43171         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43172         var ew = false;
43173         var eh = false;
43174         
43175         if(this.el ){
43176             if(typeof w == 'number'){
43177                 var aw = w - this.wrap.getFrameWidth('lr');
43178                 this.el.setWidth(this.adjustWidth('textarea', aw));
43179                 ew = aw;
43180             }
43181             if(typeof h == 'number'){
43182                 var tbh = 0;
43183                 for (var i =0; i < this.toolbars.length;i++) {
43184                     // fixme - ask toolbars for heights?
43185                     tbh += this.toolbars[i].tb.el.getHeight();
43186                     if (this.toolbars[i].footer) {
43187                         tbh += this.toolbars[i].footer.el.getHeight();
43188                     }
43189                 }
43190                 
43191                 
43192                 
43193                 
43194                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43195                 ah -= 5; // knock a few pixes off for look..
43196 //                Roo.log(ah);
43197                 this.el.setHeight(this.adjustWidth('textarea', ah));
43198                 var eh = ah;
43199             }
43200         }
43201         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43202         this.editorcore.onResize(ew,eh);
43203         
43204     },
43205
43206     /**
43207      * Toggles the editor between standard and source edit mode.
43208      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43209      */
43210     toggleSourceEdit : function(sourceEditMode)
43211     {
43212         this.editorcore.toggleSourceEdit(sourceEditMode);
43213         
43214         if(this.editorcore.sourceEditMode){
43215             Roo.log('editor - showing textarea');
43216             
43217 //            Roo.log('in');
43218 //            Roo.log(this.syncValue());
43219             this.editorcore.syncValue();
43220             this.el.removeClass('x-hidden');
43221             this.el.dom.removeAttribute('tabIndex');
43222             this.el.focus();
43223             
43224             for (var i = 0; i < this.toolbars.length; i++) {
43225                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43226                     this.toolbars[i].tb.hide();
43227                     this.toolbars[i].footer.hide();
43228                 }
43229             }
43230             
43231         }else{
43232             Roo.log('editor - hiding textarea');
43233 //            Roo.log('out')
43234 //            Roo.log(this.pushValue()); 
43235             this.editorcore.pushValue();
43236             
43237             this.el.addClass('x-hidden');
43238             this.el.dom.setAttribute('tabIndex', -1);
43239             
43240             for (var i = 0; i < this.toolbars.length; i++) {
43241                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43242                     this.toolbars[i].tb.show();
43243                     this.toolbars[i].footer.show();
43244                 }
43245             }
43246             
43247             //this.deferFocus();
43248         }
43249         
43250         this.setSize(this.wrap.getSize());
43251         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43252         
43253         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43254     },
43255  
43256     // private (for BoxComponent)
43257     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43258
43259     // private (for BoxComponent)
43260     getResizeEl : function(){
43261         return this.wrap;
43262     },
43263
43264     // private (for BoxComponent)
43265     getPositionEl : function(){
43266         return this.wrap;
43267     },
43268
43269     // private
43270     initEvents : function(){
43271         this.originalValue = this.getValue();
43272     },
43273
43274     /**
43275      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43276      * @method
43277      */
43278     markInvalid : Roo.emptyFn,
43279     /**
43280      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43281      * @method
43282      */
43283     clearInvalid : Roo.emptyFn,
43284
43285     setValue : function(v){
43286         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43287         this.editorcore.pushValue();
43288     },
43289
43290      
43291     // private
43292     deferFocus : function(){
43293         this.focus.defer(10, this);
43294     },
43295
43296     // doc'ed in Field
43297     focus : function(){
43298         this.editorcore.focus();
43299         
43300     },
43301       
43302
43303     // private
43304     onDestroy : function(){
43305         
43306         
43307         
43308         if(this.rendered){
43309             
43310             for (var i =0; i < this.toolbars.length;i++) {
43311                 // fixme - ask toolbars for heights?
43312                 this.toolbars[i].onDestroy();
43313             }
43314             
43315             this.wrap.dom.innerHTML = '';
43316             this.wrap.remove();
43317         }
43318     },
43319
43320     // private
43321     onFirstFocus : function(){
43322         //Roo.log("onFirstFocus");
43323         this.editorcore.onFirstFocus();
43324          for (var i =0; i < this.toolbars.length;i++) {
43325             this.toolbars[i].onFirstFocus();
43326         }
43327         
43328     },
43329     
43330     // private
43331     syncValue : function()
43332     {
43333         this.editorcore.syncValue();
43334     },
43335     
43336     pushValue : function()
43337     {
43338         this.editorcore.pushValue();
43339     },
43340     
43341     setStylesheets : function(stylesheets)
43342     {
43343         this.editorcore.setStylesheets(stylesheets);
43344     },
43345     
43346     removeStylesheets : function()
43347     {
43348         this.editorcore.removeStylesheets();
43349     }
43350      
43351     
43352     // hide stuff that is not compatible
43353     /**
43354      * @event blur
43355      * @hide
43356      */
43357     /**
43358      * @event change
43359      * @hide
43360      */
43361     /**
43362      * @event focus
43363      * @hide
43364      */
43365     /**
43366      * @event specialkey
43367      * @hide
43368      */
43369     /**
43370      * @cfg {String} fieldClass @hide
43371      */
43372     /**
43373      * @cfg {String} focusClass @hide
43374      */
43375     /**
43376      * @cfg {String} autoCreate @hide
43377      */
43378     /**
43379      * @cfg {String} inputType @hide
43380      */
43381     /**
43382      * @cfg {String} invalidClass @hide
43383      */
43384     /**
43385      * @cfg {String} invalidText @hide
43386      */
43387     /**
43388      * @cfg {String} msgFx @hide
43389      */
43390     /**
43391      * @cfg {String} validateOnBlur @hide
43392      */
43393 });
43394  
43395     // <script type="text/javascript">
43396 /*
43397  * Based on
43398  * Ext JS Library 1.1.1
43399  * Copyright(c) 2006-2007, Ext JS, LLC.
43400  *  
43401  
43402  */
43403
43404 /**
43405  * @class Roo.form.HtmlEditorToolbar1
43406  * Basic Toolbar
43407  * 
43408  * Usage:
43409  *
43410  new Roo.form.HtmlEditor({
43411     ....
43412     toolbars : [
43413         new Roo.form.HtmlEditorToolbar1({
43414             disable : { fonts: 1 , format: 1, ..., ... , ...],
43415             btns : [ .... ]
43416         })
43417     }
43418      
43419  * 
43420  * @cfg {Object} disable List of elements to disable..
43421  * @cfg {Array} btns List of additional buttons.
43422  * 
43423  * 
43424  * NEEDS Extra CSS? 
43425  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43426  */
43427  
43428 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43429 {
43430     
43431     Roo.apply(this, config);
43432     
43433     // default disabled, based on 'good practice'..
43434     this.disable = this.disable || {};
43435     Roo.applyIf(this.disable, {
43436         fontSize : true,
43437         colors : true,
43438         specialElements : true
43439     });
43440     
43441     
43442     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43443     // dont call parent... till later.
43444 }
43445
43446 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43447     
43448     tb: false,
43449     
43450     rendered: false,
43451     
43452     editor : false,
43453     editorcore : false,
43454     /**
43455      * @cfg {Object} disable  List of toolbar elements to disable
43456          
43457      */
43458     disable : false,
43459     
43460     
43461      /**
43462      * @cfg {String} createLinkText The default text for the create link prompt
43463      */
43464     createLinkText : 'Please enter the URL for the link:',
43465     /**
43466      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43467      */
43468     defaultLinkValue : 'http:/'+'/',
43469    
43470     
43471       /**
43472      * @cfg {Array} fontFamilies An array of available font families
43473      */
43474     fontFamilies : [
43475         'Arial',
43476         'Courier New',
43477         'Tahoma',
43478         'Times New Roman',
43479         'Verdana'
43480     ],
43481     
43482     specialChars : [
43483            "&#169;",
43484           "&#174;",     
43485           "&#8482;",    
43486           "&#163;" ,    
43487          // "&#8212;",    
43488           "&#8230;",    
43489           "&#247;" ,    
43490         //  "&#225;" ,     ?? a acute?
43491            "&#8364;"    , //Euro
43492        //   "&#8220;"    ,
43493         //  "&#8221;"    ,
43494         //  "&#8226;"    ,
43495           "&#176;"  //   , // degrees
43496
43497          // "&#233;"     , // e ecute
43498          // "&#250;"     , // u ecute?
43499     ],
43500     
43501     specialElements : [
43502         {
43503             text: "Insert Table",
43504             xtype: 'MenuItem',
43505             xns : Roo.Menu,
43506             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43507                 
43508         },
43509         {    
43510             text: "Insert Image",
43511             xtype: 'MenuItem',
43512             xns : Roo.Menu,
43513             ihtml : '<img src="about:blank"/>'
43514             
43515         }
43516         
43517          
43518     ],
43519     
43520     
43521     inputElements : [ 
43522             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43523             "input:submit", "input:button", "select", "textarea", "label" ],
43524     formats : [
43525         ["p"] ,  
43526         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43527         ["pre"],[ "code"], 
43528         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43529         ['div'],['span']
43530     ],
43531     
43532     cleanStyles : [
43533         "font-size"
43534     ],
43535      /**
43536      * @cfg {String} defaultFont default font to use.
43537      */
43538     defaultFont: 'tahoma',
43539    
43540     fontSelect : false,
43541     
43542     
43543     formatCombo : false,
43544     
43545     init : function(editor)
43546     {
43547         this.editor = editor;
43548         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43549         var editorcore = this.editorcore;
43550         
43551         var _t = this;
43552         
43553         var fid = editorcore.frameId;
43554         var etb = this;
43555         function btn(id, toggle, handler){
43556             var xid = fid + '-'+ id ;
43557             return {
43558                 id : xid,
43559                 cmd : id,
43560                 cls : 'x-btn-icon x-edit-'+id,
43561                 enableToggle:toggle !== false,
43562                 scope: _t, // was editor...
43563                 handler:handler||_t.relayBtnCmd,
43564                 clickEvent:'mousedown',
43565                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43566                 tabIndex:-1
43567             };
43568         }
43569         
43570         
43571         
43572         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43573         this.tb = tb;
43574          // stop form submits
43575         tb.el.on('click', function(e){
43576             e.preventDefault(); // what does this do?
43577         });
43578
43579         if(!this.disable.font) { // && !Roo.isSafari){
43580             /* why no safari for fonts 
43581             editor.fontSelect = tb.el.createChild({
43582                 tag:'select',
43583                 tabIndex: -1,
43584                 cls:'x-font-select',
43585                 html: this.createFontOptions()
43586             });
43587             
43588             editor.fontSelect.on('change', function(){
43589                 var font = editor.fontSelect.dom.value;
43590                 editor.relayCmd('fontname', font);
43591                 editor.deferFocus();
43592             }, editor);
43593             
43594             tb.add(
43595                 editor.fontSelect.dom,
43596                 '-'
43597             );
43598             */
43599             
43600         };
43601         if(!this.disable.formats){
43602             this.formatCombo = new Roo.form.ComboBox({
43603                 store: new Roo.data.SimpleStore({
43604                     id : 'tag',
43605                     fields: ['tag'],
43606                     data : this.formats // from states.js
43607                 }),
43608                 blockFocus : true,
43609                 name : '',
43610                 //autoCreate : {tag: "div",  size: "20"},
43611                 displayField:'tag',
43612                 typeAhead: false,
43613                 mode: 'local',
43614                 editable : false,
43615                 triggerAction: 'all',
43616                 emptyText:'Add tag',
43617                 selectOnFocus:true,
43618                 width:135,
43619                 listeners : {
43620                     'select': function(c, r, i) {
43621                         editorcore.insertTag(r.get('tag'));
43622                         editor.focus();
43623                     }
43624                 }
43625
43626             });
43627             tb.addField(this.formatCombo);
43628             
43629         }
43630         
43631         if(!this.disable.format){
43632             tb.add(
43633                 btn('bold'),
43634                 btn('italic'),
43635                 btn('underline')
43636             );
43637         };
43638         if(!this.disable.fontSize){
43639             tb.add(
43640                 '-',
43641                 
43642                 
43643                 btn('increasefontsize', false, editorcore.adjustFont),
43644                 btn('decreasefontsize', false, editorcore.adjustFont)
43645             );
43646         };
43647         
43648         
43649         if(!this.disable.colors){
43650             tb.add(
43651                 '-', {
43652                     id:editorcore.frameId +'-forecolor',
43653                     cls:'x-btn-icon x-edit-forecolor',
43654                     clickEvent:'mousedown',
43655                     tooltip: this.buttonTips['forecolor'] || undefined,
43656                     tabIndex:-1,
43657                     menu : new Roo.menu.ColorMenu({
43658                         allowReselect: true,
43659                         focus: Roo.emptyFn,
43660                         value:'000000',
43661                         plain:true,
43662                         selectHandler: function(cp, color){
43663                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43664                             editor.deferFocus();
43665                         },
43666                         scope: editorcore,
43667                         clickEvent:'mousedown'
43668                     })
43669                 }, {
43670                     id:editorcore.frameId +'backcolor',
43671                     cls:'x-btn-icon x-edit-backcolor',
43672                     clickEvent:'mousedown',
43673                     tooltip: this.buttonTips['backcolor'] || undefined,
43674                     tabIndex:-1,
43675                     menu : new Roo.menu.ColorMenu({
43676                         focus: Roo.emptyFn,
43677                         value:'FFFFFF',
43678                         plain:true,
43679                         allowReselect: true,
43680                         selectHandler: function(cp, color){
43681                             if(Roo.isGecko){
43682                                 editorcore.execCmd('useCSS', false);
43683                                 editorcore.execCmd('hilitecolor', color);
43684                                 editorcore.execCmd('useCSS', true);
43685                                 editor.deferFocus();
43686                             }else{
43687                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43688                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43689                                 editor.deferFocus();
43690                             }
43691                         },
43692                         scope:editorcore,
43693                         clickEvent:'mousedown'
43694                     })
43695                 }
43696             );
43697         };
43698         // now add all the items...
43699         
43700
43701         if(!this.disable.alignments){
43702             tb.add(
43703                 '-',
43704                 btn('justifyleft'),
43705                 btn('justifycenter'),
43706                 btn('justifyright')
43707             );
43708         };
43709
43710         //if(!Roo.isSafari){
43711             if(!this.disable.links){
43712                 tb.add(
43713                     '-',
43714                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43715                 );
43716             };
43717
43718             if(!this.disable.lists){
43719                 tb.add(
43720                     '-',
43721                     btn('insertorderedlist'),
43722                     btn('insertunorderedlist')
43723                 );
43724             }
43725             if(!this.disable.sourceEdit){
43726                 tb.add(
43727                     '-',
43728                     btn('sourceedit', true, function(btn){
43729                         this.toggleSourceEdit(btn.pressed);
43730                     })
43731                 );
43732             }
43733         //}
43734         
43735         var smenu = { };
43736         // special menu.. - needs to be tidied up..
43737         if (!this.disable.special) {
43738             smenu = {
43739                 text: "&#169;",
43740                 cls: 'x-edit-none',
43741                 
43742                 menu : {
43743                     items : []
43744                 }
43745             };
43746             for (var i =0; i < this.specialChars.length; i++) {
43747                 smenu.menu.items.push({
43748                     
43749                     html: this.specialChars[i],
43750                     handler: function(a,b) {
43751                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43752                         //editor.insertAtCursor(a.html);
43753                         
43754                     },
43755                     tabIndex:-1
43756                 });
43757             }
43758             
43759             
43760             tb.add(smenu);
43761             
43762             
43763         }
43764         
43765         var cmenu = { };
43766         if (!this.disable.cleanStyles) {
43767             cmenu = {
43768                 cls: 'x-btn-icon x-btn-clear',
43769                 
43770                 menu : {
43771                     items : []
43772                 }
43773             };
43774             for (var i =0; i < this.cleanStyles.length; i++) {
43775                 cmenu.menu.items.push({
43776                     actiontype : this.cleanStyles[i],
43777                     html: 'Remove ' + this.cleanStyles[i],
43778                     handler: function(a,b) {
43779 //                        Roo.log(a);
43780 //                        Roo.log(b);
43781                         var c = Roo.get(editorcore.doc.body);
43782                         c.select('[style]').each(function(s) {
43783                             s.dom.style.removeProperty(a.actiontype);
43784                         });
43785                         editorcore.syncValue();
43786                     },
43787                     tabIndex:-1
43788                 });
43789             }
43790              cmenu.menu.items.push({
43791                 actiontype : 'tablewidths',
43792                 html: 'Remove Table Widths',
43793                 handler: function(a,b) {
43794                     editorcore.cleanTableWidths();
43795                     editorcore.syncValue();
43796                 },
43797                 tabIndex:-1
43798             });
43799             cmenu.menu.items.push({
43800                 actiontype : 'word',
43801                 html: 'Remove MS Word Formating',
43802                 handler: function(a,b) {
43803                     editorcore.cleanWord();
43804                     editorcore.syncValue();
43805                 },
43806                 tabIndex:-1
43807             });
43808             
43809             cmenu.menu.items.push({
43810                 actiontype : 'all',
43811                 html: 'Remove All Styles',
43812                 handler: function(a,b) {
43813                     
43814                     var c = Roo.get(editorcore.doc.body);
43815                     c.select('[style]').each(function(s) {
43816                         s.dom.removeAttribute('style');
43817                     });
43818                     editorcore.syncValue();
43819                 },
43820                 tabIndex:-1
43821             });
43822             
43823             cmenu.menu.items.push({
43824                 actiontype : 'all',
43825                 html: 'Remove All CSS Classes',
43826                 handler: function(a,b) {
43827                     
43828                     var c = Roo.get(editorcore.doc.body);
43829                     c.select('[class]').each(function(s) {
43830                         s.dom.className = '';
43831                     });
43832                     editorcore.syncValue();
43833                 },
43834                 tabIndex:-1
43835             });
43836             
43837              cmenu.menu.items.push({
43838                 actiontype : 'tidy',
43839                 html: 'Tidy HTML Source',
43840                 handler: function(a,b) {
43841                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43842                     editorcore.syncValue();
43843                 },
43844                 tabIndex:-1
43845             });
43846             
43847             
43848             tb.add(cmenu);
43849         }
43850          
43851         if (!this.disable.specialElements) {
43852             var semenu = {
43853                 text: "Other;",
43854                 cls: 'x-edit-none',
43855                 menu : {
43856                     items : []
43857                 }
43858             };
43859             for (var i =0; i < this.specialElements.length; i++) {
43860                 semenu.menu.items.push(
43861                     Roo.apply({ 
43862                         handler: function(a,b) {
43863                             editor.insertAtCursor(this.ihtml);
43864                         }
43865                     }, this.specialElements[i])
43866                 );
43867                     
43868             }
43869             
43870             tb.add(semenu);
43871             
43872             
43873         }
43874          
43875         
43876         if (this.btns) {
43877             for(var i =0; i< this.btns.length;i++) {
43878                 var b = Roo.factory(this.btns[i],Roo.form);
43879                 b.cls =  'x-edit-none';
43880                 
43881                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43882                     b.cls += ' x-init-enable';
43883                 }
43884                 
43885                 b.scope = editorcore;
43886                 tb.add(b);
43887             }
43888         
43889         }
43890         
43891         
43892         
43893         // disable everything...
43894         
43895         this.tb.items.each(function(item){
43896             
43897            if(
43898                 item.id != editorcore.frameId+ '-sourceedit' && 
43899                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43900             ){
43901                 
43902                 item.disable();
43903             }
43904         });
43905         this.rendered = true;
43906         
43907         // the all the btns;
43908         editor.on('editorevent', this.updateToolbar, this);
43909         // other toolbars need to implement this..
43910         //editor.on('editmodechange', this.updateToolbar, this);
43911     },
43912     
43913     
43914     relayBtnCmd : function(btn) {
43915         this.editorcore.relayCmd(btn.cmd);
43916     },
43917     // private used internally
43918     createLink : function(){
43919         Roo.log("create link?");
43920         var url = prompt(this.createLinkText, this.defaultLinkValue);
43921         if(url && url != 'http:/'+'/'){
43922             this.editorcore.relayCmd('createlink', url);
43923         }
43924     },
43925
43926     
43927     /**
43928      * Protected method that will not generally be called directly. It triggers
43929      * a toolbar update by reading the markup state of the current selection in the editor.
43930      */
43931     updateToolbar: function(){
43932
43933         if(!this.editorcore.activated){
43934             this.editor.onFirstFocus();
43935             return;
43936         }
43937
43938         var btns = this.tb.items.map, 
43939             doc = this.editorcore.doc,
43940             frameId = this.editorcore.frameId;
43941
43942         if(!this.disable.font && !Roo.isSafari){
43943             /*
43944             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43945             if(name != this.fontSelect.dom.value){
43946                 this.fontSelect.dom.value = name;
43947             }
43948             */
43949         }
43950         if(!this.disable.format){
43951             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43952             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43953             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43954         }
43955         if(!this.disable.alignments){
43956             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43957             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43958             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43959         }
43960         if(!Roo.isSafari && !this.disable.lists){
43961             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43962             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43963         }
43964         
43965         var ans = this.editorcore.getAllAncestors();
43966         if (this.formatCombo) {
43967             
43968             
43969             var store = this.formatCombo.store;
43970             this.formatCombo.setValue("");
43971             for (var i =0; i < ans.length;i++) {
43972                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43973                     // select it..
43974                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43975                     break;
43976                 }
43977             }
43978         }
43979         
43980         
43981         
43982         // hides menus... - so this cant be on a menu...
43983         Roo.menu.MenuMgr.hideAll();
43984
43985         //this.editorsyncValue();
43986     },
43987    
43988     
43989     createFontOptions : function(){
43990         var buf = [], fs = this.fontFamilies, ff, lc;
43991         
43992         
43993         
43994         for(var i = 0, len = fs.length; i< len; i++){
43995             ff = fs[i];
43996             lc = ff.toLowerCase();
43997             buf.push(
43998                 '<option value="',lc,'" style="font-family:',ff,';"',
43999                     (this.defaultFont == lc ? ' selected="true">' : '>'),
44000                     ff,
44001                 '</option>'
44002             );
44003         }
44004         return buf.join('');
44005     },
44006     
44007     toggleSourceEdit : function(sourceEditMode){
44008         
44009         Roo.log("toolbar toogle");
44010         if(sourceEditMode === undefined){
44011             sourceEditMode = !this.sourceEditMode;
44012         }
44013         this.sourceEditMode = sourceEditMode === true;
44014         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44015         // just toggle the button?
44016         if(btn.pressed !== this.sourceEditMode){
44017             btn.toggle(this.sourceEditMode);
44018             return;
44019         }
44020         
44021         if(sourceEditMode){
44022             Roo.log("disabling buttons");
44023             this.tb.items.each(function(item){
44024                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44025                     item.disable();
44026                 }
44027             });
44028           
44029         }else{
44030             Roo.log("enabling buttons");
44031             if(this.editorcore.initialized){
44032                 this.tb.items.each(function(item){
44033                     item.enable();
44034                 });
44035             }
44036             
44037         }
44038         Roo.log("calling toggole on editor");
44039         // tell the editor that it's been pressed..
44040         this.editor.toggleSourceEdit(sourceEditMode);
44041        
44042     },
44043      /**
44044      * Object collection of toolbar tooltips for the buttons in the editor. The key
44045      * is the command id associated with that button and the value is a valid QuickTips object.
44046      * For example:
44047 <pre><code>
44048 {
44049     bold : {
44050         title: 'Bold (Ctrl+B)',
44051         text: 'Make the selected text bold.',
44052         cls: 'x-html-editor-tip'
44053     },
44054     italic : {
44055         title: 'Italic (Ctrl+I)',
44056         text: 'Make the selected text italic.',
44057         cls: 'x-html-editor-tip'
44058     },
44059     ...
44060 </code></pre>
44061     * @type Object
44062      */
44063     buttonTips : {
44064         bold : {
44065             title: 'Bold (Ctrl+B)',
44066             text: 'Make the selected text bold.',
44067             cls: 'x-html-editor-tip'
44068         },
44069         italic : {
44070             title: 'Italic (Ctrl+I)',
44071             text: 'Make the selected text italic.',
44072             cls: 'x-html-editor-tip'
44073         },
44074         underline : {
44075             title: 'Underline (Ctrl+U)',
44076             text: 'Underline the selected text.',
44077             cls: 'x-html-editor-tip'
44078         },
44079         increasefontsize : {
44080             title: 'Grow Text',
44081             text: 'Increase the font size.',
44082             cls: 'x-html-editor-tip'
44083         },
44084         decreasefontsize : {
44085             title: 'Shrink Text',
44086             text: 'Decrease the font size.',
44087             cls: 'x-html-editor-tip'
44088         },
44089         backcolor : {
44090             title: 'Text Highlight Color',
44091             text: 'Change the background color of the selected text.',
44092             cls: 'x-html-editor-tip'
44093         },
44094         forecolor : {
44095             title: 'Font Color',
44096             text: 'Change the color of the selected text.',
44097             cls: 'x-html-editor-tip'
44098         },
44099         justifyleft : {
44100             title: 'Align Text Left',
44101             text: 'Align text to the left.',
44102             cls: 'x-html-editor-tip'
44103         },
44104         justifycenter : {
44105             title: 'Center Text',
44106             text: 'Center text in the editor.',
44107             cls: 'x-html-editor-tip'
44108         },
44109         justifyright : {
44110             title: 'Align Text Right',
44111             text: 'Align text to the right.',
44112             cls: 'x-html-editor-tip'
44113         },
44114         insertunorderedlist : {
44115             title: 'Bullet List',
44116             text: 'Start a bulleted list.',
44117             cls: 'x-html-editor-tip'
44118         },
44119         insertorderedlist : {
44120             title: 'Numbered List',
44121             text: 'Start a numbered list.',
44122             cls: 'x-html-editor-tip'
44123         },
44124         createlink : {
44125             title: 'Hyperlink',
44126             text: 'Make the selected text a hyperlink.',
44127             cls: 'x-html-editor-tip'
44128         },
44129         sourceedit : {
44130             title: 'Source Edit',
44131             text: 'Switch to source editing mode.',
44132             cls: 'x-html-editor-tip'
44133         }
44134     },
44135     // private
44136     onDestroy : function(){
44137         if(this.rendered){
44138             
44139             this.tb.items.each(function(item){
44140                 if(item.menu){
44141                     item.menu.removeAll();
44142                     if(item.menu.el){
44143                         item.menu.el.destroy();
44144                     }
44145                 }
44146                 item.destroy();
44147             });
44148              
44149         }
44150     },
44151     onFirstFocus: function() {
44152         this.tb.items.each(function(item){
44153            item.enable();
44154         });
44155     }
44156 });
44157
44158
44159
44160
44161 // <script type="text/javascript">
44162 /*
44163  * Based on
44164  * Ext JS Library 1.1.1
44165  * Copyright(c) 2006-2007, Ext JS, LLC.
44166  *  
44167  
44168  */
44169
44170  
44171 /**
44172  * @class Roo.form.HtmlEditor.ToolbarContext
44173  * Context Toolbar
44174  * 
44175  * Usage:
44176  *
44177  new Roo.form.HtmlEditor({
44178     ....
44179     toolbars : [
44180         { xtype: 'ToolbarStandard', styles : {} }
44181         { xtype: 'ToolbarContext', disable : {} }
44182     ]
44183 })
44184
44185      
44186  * 
44187  * @config : {Object} disable List of elements to disable.. (not done yet.)
44188  * @config : {Object} styles  Map of styles available.
44189  * 
44190  */
44191
44192 Roo.form.HtmlEditor.ToolbarContext = function(config)
44193 {
44194     
44195     Roo.apply(this, config);
44196     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44197     // dont call parent... till later.
44198     this.styles = this.styles || {};
44199 }
44200
44201  
44202
44203 Roo.form.HtmlEditor.ToolbarContext.types = {
44204     'IMG' : {
44205         width : {
44206             title: "Width",
44207             width: 40
44208         },
44209         height:  {
44210             title: "Height",
44211             width: 40
44212         },
44213         align: {
44214             title: "Align",
44215             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44216             width : 80
44217             
44218         },
44219         border: {
44220             title: "Border",
44221             width: 40
44222         },
44223         alt: {
44224             title: "Alt",
44225             width: 120
44226         },
44227         src : {
44228             title: "Src",
44229             width: 220
44230         }
44231         
44232     },
44233     'A' : {
44234         name : {
44235             title: "Name",
44236             width: 50
44237         },
44238         target:  {
44239             title: "Target",
44240             width: 120
44241         },
44242         href:  {
44243             title: "Href",
44244             width: 220
44245         } // border?
44246         
44247     },
44248     'TABLE' : {
44249         rows : {
44250             title: "Rows",
44251             width: 20
44252         },
44253         cols : {
44254             title: "Cols",
44255             width: 20
44256         },
44257         width : {
44258             title: "Width",
44259             width: 40
44260         },
44261         height : {
44262             title: "Height",
44263             width: 40
44264         },
44265         border : {
44266             title: "Border",
44267             width: 20
44268         }
44269     },
44270     'TD' : {
44271         width : {
44272             title: "Width",
44273             width: 40
44274         },
44275         height : {
44276             title: "Height",
44277             width: 40
44278         },   
44279         align: {
44280             title: "Align",
44281             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44282             width: 80
44283         },
44284         valign: {
44285             title: "Valign",
44286             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44287             width: 80
44288         },
44289         colspan: {
44290             title: "Colspan",
44291             width: 20
44292             
44293         },
44294          'font-family'  : {
44295             title : "Font",
44296             style : 'fontFamily',
44297             displayField: 'display',
44298             optname : 'font-family',
44299             width: 140
44300         }
44301     },
44302     'INPUT' : {
44303         name : {
44304             title: "name",
44305             width: 120
44306         },
44307         value : {
44308             title: "Value",
44309             width: 120
44310         },
44311         width : {
44312             title: "Width",
44313             width: 40
44314         }
44315     },
44316     'LABEL' : {
44317         'for' : {
44318             title: "For",
44319             width: 120
44320         }
44321     },
44322     'TEXTAREA' : {
44323           name : {
44324             title: "name",
44325             width: 120
44326         },
44327         rows : {
44328             title: "Rows",
44329             width: 20
44330         },
44331         cols : {
44332             title: "Cols",
44333             width: 20
44334         }
44335     },
44336     'SELECT' : {
44337         name : {
44338             title: "name",
44339             width: 120
44340         },
44341         selectoptions : {
44342             title: "Options",
44343             width: 200
44344         }
44345     },
44346     
44347     // should we really allow this??
44348     // should this just be 
44349     'BODY' : {
44350         title : {
44351             title: "Title",
44352             width: 200,
44353             disabled : true
44354         }
44355     },
44356     'SPAN' : {
44357         'font-family'  : {
44358             title : "Font",
44359             style : 'fontFamily',
44360             displayField: 'display',
44361             optname : 'font-family',
44362             width: 140
44363         }
44364     },
44365     'DIV' : {
44366         'font-family'  : {
44367             title : "Font",
44368             style : 'fontFamily',
44369             displayField: 'display',
44370             optname : 'font-family',
44371             width: 140
44372         }
44373     },
44374      'P' : {
44375         'font-family'  : {
44376             title : "Font",
44377             style : 'fontFamily',
44378             displayField: 'display',
44379             optname : 'font-family',
44380             width: 140
44381         }
44382     },
44383     
44384     '*' : {
44385         // empty..
44386     }
44387
44388 };
44389
44390 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44391 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44392
44393 Roo.form.HtmlEditor.ToolbarContext.options = {
44394         'font-family'  : [ 
44395                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44396                 [ 'Courier New', 'Courier New'],
44397                 [ 'Tahoma', 'Tahoma'],
44398                 [ 'Times New Roman,serif', 'Times'],
44399                 [ 'Verdana','Verdana' ]
44400         ]
44401 };
44402
44403 // fixme - these need to be configurable..
44404  
44405
44406 //Roo.form.HtmlEditor.ToolbarContext.types
44407
44408
44409 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44410     
44411     tb: false,
44412     
44413     rendered: false,
44414     
44415     editor : false,
44416     editorcore : false,
44417     /**
44418      * @cfg {Object} disable  List of toolbar elements to disable
44419          
44420      */
44421     disable : false,
44422     /**
44423      * @cfg {Object} styles List of styles 
44424      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44425      *
44426      * These must be defined in the page, so they get rendered correctly..
44427      * .headline { }
44428      * TD.underline { }
44429      * 
44430      */
44431     styles : false,
44432     
44433     options: false,
44434     
44435     toolbars : false,
44436     
44437     init : function(editor)
44438     {
44439         this.editor = editor;
44440         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44441         var editorcore = this.editorcore;
44442         
44443         var fid = editorcore.frameId;
44444         var etb = this;
44445         function btn(id, toggle, handler){
44446             var xid = fid + '-'+ id ;
44447             return {
44448                 id : xid,
44449                 cmd : id,
44450                 cls : 'x-btn-icon x-edit-'+id,
44451                 enableToggle:toggle !== false,
44452                 scope: editorcore, // was editor...
44453                 handler:handler||editorcore.relayBtnCmd,
44454                 clickEvent:'mousedown',
44455                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44456                 tabIndex:-1
44457             };
44458         }
44459         // create a new element.
44460         var wdiv = editor.wrap.createChild({
44461                 tag: 'div'
44462             }, editor.wrap.dom.firstChild.nextSibling, true);
44463         
44464         // can we do this more than once??
44465         
44466          // stop form submits
44467       
44468  
44469         // disable everything...
44470         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44471         this.toolbars = {};
44472            
44473         for (var i in  ty) {
44474           
44475             this.toolbars[i] = this.buildToolbar(ty[i],i);
44476         }
44477         this.tb = this.toolbars.BODY;
44478         this.tb.el.show();
44479         this.buildFooter();
44480         this.footer.show();
44481         editor.on('hide', function( ) { this.footer.hide() }, this);
44482         editor.on('show', function( ) { this.footer.show() }, this);
44483         
44484          
44485         this.rendered = true;
44486         
44487         // the all the btns;
44488         editor.on('editorevent', this.updateToolbar, this);
44489         // other toolbars need to implement this..
44490         //editor.on('editmodechange', this.updateToolbar, this);
44491     },
44492     
44493     
44494     
44495     /**
44496      * Protected method that will not generally be called directly. It triggers
44497      * a toolbar update by reading the markup state of the current selection in the editor.
44498      *
44499      * Note you can force an update by calling on('editorevent', scope, false)
44500      */
44501     updateToolbar: function(editor,ev,sel){
44502
44503         //Roo.log(ev);
44504         // capture mouse up - this is handy for selecting images..
44505         // perhaps should go somewhere else...
44506         if(!this.editorcore.activated){
44507              this.editor.onFirstFocus();
44508             return;
44509         }
44510         
44511         
44512         
44513         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44514         // selectNode - might want to handle IE?
44515         if (ev &&
44516             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44517             ev.target && ev.target.tagName == 'IMG') {
44518             // they have click on an image...
44519             // let's see if we can change the selection...
44520             sel = ev.target;
44521          
44522               var nodeRange = sel.ownerDocument.createRange();
44523             try {
44524                 nodeRange.selectNode(sel);
44525             } catch (e) {
44526                 nodeRange.selectNodeContents(sel);
44527             }
44528             //nodeRange.collapse(true);
44529             var s = this.editorcore.win.getSelection();
44530             s.removeAllRanges();
44531             s.addRange(nodeRange);
44532         }  
44533         
44534       
44535         var updateFooter = sel ? false : true;
44536         
44537         
44538         var ans = this.editorcore.getAllAncestors();
44539         
44540         // pick
44541         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44542         
44543         if (!sel) { 
44544             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44545             sel = sel ? sel : this.editorcore.doc.body;
44546             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44547             
44548         }
44549         // pick a menu that exists..
44550         var tn = sel.tagName.toUpperCase();
44551         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44552         
44553         tn = sel.tagName.toUpperCase();
44554         
44555         var lastSel = this.tb.selectedNode;
44556         
44557         this.tb.selectedNode = sel;
44558         
44559         // if current menu does not match..
44560         
44561         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44562                 
44563             this.tb.el.hide();
44564             ///console.log("show: " + tn);
44565             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44566             this.tb.el.show();
44567             // update name
44568             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44569             
44570             
44571             // update attributes
44572             if (this.tb.fields) {
44573                 this.tb.fields.each(function(e) {
44574                     if (e.stylename) {
44575                         e.setValue(sel.style[e.stylename]);
44576                         return;
44577                     } 
44578                    e.setValue(sel.getAttribute(e.attrname));
44579                 });
44580             }
44581             
44582             var hasStyles = false;
44583             for(var i in this.styles) {
44584                 hasStyles = true;
44585                 break;
44586             }
44587             
44588             // update styles
44589             if (hasStyles) { 
44590                 var st = this.tb.fields.item(0);
44591                 
44592                 st.store.removeAll();
44593                
44594                 
44595                 var cn = sel.className.split(/\s+/);
44596                 
44597                 var avs = [];
44598                 if (this.styles['*']) {
44599                     
44600                     Roo.each(this.styles['*'], function(v) {
44601                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44602                     });
44603                 }
44604                 if (this.styles[tn]) { 
44605                     Roo.each(this.styles[tn], function(v) {
44606                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44607                     });
44608                 }
44609                 
44610                 st.store.loadData(avs);
44611                 st.collapse();
44612                 st.setValue(cn);
44613             }
44614             // flag our selected Node.
44615             this.tb.selectedNode = sel;
44616            
44617            
44618             Roo.menu.MenuMgr.hideAll();
44619
44620         }
44621         
44622         if (!updateFooter) {
44623             //this.footDisp.dom.innerHTML = ''; 
44624             return;
44625         }
44626         // update the footer
44627         //
44628         var html = '';
44629         
44630         this.footerEls = ans.reverse();
44631         Roo.each(this.footerEls, function(a,i) {
44632             if (!a) { return; }
44633             html += html.length ? ' &gt; '  :  '';
44634             
44635             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44636             
44637         });
44638        
44639         // 
44640         var sz = this.footDisp.up('td').getSize();
44641         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44642         this.footDisp.dom.style.marginLeft = '5px';
44643         
44644         this.footDisp.dom.style.overflow = 'hidden';
44645         
44646         this.footDisp.dom.innerHTML = html;
44647             
44648         //this.editorsyncValue();
44649     },
44650      
44651     
44652    
44653        
44654     // private
44655     onDestroy : function(){
44656         if(this.rendered){
44657             
44658             this.tb.items.each(function(item){
44659                 if(item.menu){
44660                     item.menu.removeAll();
44661                     if(item.menu.el){
44662                         item.menu.el.destroy();
44663                     }
44664                 }
44665                 item.destroy();
44666             });
44667              
44668         }
44669     },
44670     onFirstFocus: function() {
44671         // need to do this for all the toolbars..
44672         this.tb.items.each(function(item){
44673            item.enable();
44674         });
44675     },
44676     buildToolbar: function(tlist, nm)
44677     {
44678         var editor = this.editor;
44679         var editorcore = this.editorcore;
44680          // create a new element.
44681         var wdiv = editor.wrap.createChild({
44682                 tag: 'div'
44683             }, editor.wrap.dom.firstChild.nextSibling, true);
44684         
44685        
44686         var tb = new Roo.Toolbar(wdiv);
44687         // add the name..
44688         
44689         tb.add(nm+ ":&nbsp;");
44690         
44691         var styles = [];
44692         for(var i in this.styles) {
44693             styles.push(i);
44694         }
44695         
44696         // styles...
44697         if (styles && styles.length) {
44698             
44699             // this needs a multi-select checkbox...
44700             tb.addField( new Roo.form.ComboBox({
44701                 store: new Roo.data.SimpleStore({
44702                     id : 'val',
44703                     fields: ['val', 'selected'],
44704                     data : [] 
44705                 }),
44706                 name : '-roo-edit-className',
44707                 attrname : 'className',
44708                 displayField: 'val',
44709                 typeAhead: false,
44710                 mode: 'local',
44711                 editable : false,
44712                 triggerAction: 'all',
44713                 emptyText:'Select Style',
44714                 selectOnFocus:true,
44715                 width: 130,
44716                 listeners : {
44717                     'select': function(c, r, i) {
44718                         // initial support only for on class per el..
44719                         tb.selectedNode.className =  r ? r.get('val') : '';
44720                         editorcore.syncValue();
44721                     }
44722                 }
44723     
44724             }));
44725         }
44726         
44727         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44728         var tbops = tbc.options;
44729         
44730         for (var i in tlist) {
44731             
44732             var item = tlist[i];
44733             tb.add(item.title + ":&nbsp;");
44734             
44735             
44736             //optname == used so you can configure the options available..
44737             var opts = item.opts ? item.opts : false;
44738             if (item.optname) {
44739                 opts = tbops[item.optname];
44740            
44741             }
44742             
44743             if (opts) {
44744                 // opts == pulldown..
44745                 tb.addField( new Roo.form.ComboBox({
44746                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44747                         id : 'val',
44748                         fields: ['val', 'display'],
44749                         data : opts  
44750                     }),
44751                     name : '-roo-edit-' + i,
44752                     attrname : i,
44753                     stylename : item.style ? item.style : false,
44754                     displayField: item.displayField ? item.displayField : 'val',
44755                     valueField :  'val',
44756                     typeAhead: false,
44757                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44758                     editable : false,
44759                     triggerAction: 'all',
44760                     emptyText:'Select',
44761                     selectOnFocus:true,
44762                     width: item.width ? item.width  : 130,
44763                     listeners : {
44764                         'select': function(c, r, i) {
44765                             if (c.stylename) {
44766                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44767                                 return;
44768                             }
44769                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44770                         }
44771                     }
44772
44773                 }));
44774                 continue;
44775                     
44776                  
44777                 
44778                 tb.addField( new Roo.form.TextField({
44779                     name: i,
44780                     width: 100,
44781                     //allowBlank:false,
44782                     value: ''
44783                 }));
44784                 continue;
44785             }
44786             tb.addField( new Roo.form.TextField({
44787                 name: '-roo-edit-' + i,
44788                 attrname : i,
44789                 
44790                 width: item.width,
44791                 //allowBlank:true,
44792                 value: '',
44793                 listeners: {
44794                     'change' : function(f, nv, ov) {
44795                         tb.selectedNode.setAttribute(f.attrname, nv);
44796                     }
44797                 }
44798             }));
44799              
44800         }
44801         
44802         var _this = this;
44803         
44804         if(nm == 'BODY'){
44805             tb.addSeparator();
44806         
44807             tb.addButton( {
44808                 text: 'Stylesheets',
44809
44810                 listeners : {
44811                     click : function ()
44812                     {
44813                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44814                     }
44815                 }
44816             });
44817         }
44818         
44819         tb.addFill();
44820         tb.addButton( {
44821             text: 'Remove Tag',
44822     
44823             listeners : {
44824                 click : function ()
44825                 {
44826                     // remove
44827                     // undo does not work.
44828                      
44829                     var sn = tb.selectedNode;
44830                     
44831                     var pn = sn.parentNode;
44832                     
44833                     var stn =  sn.childNodes[0];
44834                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44835                     while (sn.childNodes.length) {
44836                         var node = sn.childNodes[0];
44837                         sn.removeChild(node);
44838                         //Roo.log(node);
44839                         pn.insertBefore(node, sn);
44840                         
44841                     }
44842                     pn.removeChild(sn);
44843                     var range = editorcore.createRange();
44844         
44845                     range.setStart(stn,0);
44846                     range.setEnd(en,0); //????
44847                     //range.selectNode(sel);
44848                     
44849                     
44850                     var selection = editorcore.getSelection();
44851                     selection.removeAllRanges();
44852                     selection.addRange(range);
44853                     
44854                     
44855                     
44856                     //_this.updateToolbar(null, null, pn);
44857                     _this.updateToolbar(null, null, null);
44858                     _this.footDisp.dom.innerHTML = ''; 
44859                 }
44860             }
44861             
44862                     
44863                 
44864             
44865         });
44866         
44867         
44868         tb.el.on('click', function(e){
44869             e.preventDefault(); // what does this do?
44870         });
44871         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44872         tb.el.hide();
44873         tb.name = nm;
44874         // dont need to disable them... as they will get hidden
44875         return tb;
44876          
44877         
44878     },
44879     buildFooter : function()
44880     {
44881         
44882         var fel = this.editor.wrap.createChild();
44883         this.footer = new Roo.Toolbar(fel);
44884         // toolbar has scrolly on left / right?
44885         var footDisp= new Roo.Toolbar.Fill();
44886         var _t = this;
44887         this.footer.add(
44888             {
44889                 text : '&lt;',
44890                 xtype: 'Button',
44891                 handler : function() {
44892                     _t.footDisp.scrollTo('left',0,true)
44893                 }
44894             }
44895         );
44896         this.footer.add( footDisp );
44897         this.footer.add( 
44898             {
44899                 text : '&gt;',
44900                 xtype: 'Button',
44901                 handler : function() {
44902                     // no animation..
44903                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44904                 }
44905             }
44906         );
44907         var fel = Roo.get(footDisp.el);
44908         fel.addClass('x-editor-context');
44909         this.footDispWrap = fel; 
44910         this.footDispWrap.overflow  = 'hidden';
44911         
44912         this.footDisp = fel.createChild();
44913         this.footDispWrap.on('click', this.onContextClick, this)
44914         
44915         
44916     },
44917     onContextClick : function (ev,dom)
44918     {
44919         ev.preventDefault();
44920         var  cn = dom.className;
44921         //Roo.log(cn);
44922         if (!cn.match(/x-ed-loc-/)) {
44923             return;
44924         }
44925         var n = cn.split('-').pop();
44926         var ans = this.footerEls;
44927         var sel = ans[n];
44928         
44929          // pick
44930         var range = this.editorcore.createRange();
44931         
44932         range.selectNodeContents(sel);
44933         //range.selectNode(sel);
44934         
44935         
44936         var selection = this.editorcore.getSelection();
44937         selection.removeAllRanges();
44938         selection.addRange(range);
44939         
44940         
44941         
44942         this.updateToolbar(null, null, sel);
44943         
44944         
44945     }
44946     
44947     
44948     
44949     
44950     
44951 });
44952
44953
44954
44955
44956
44957 /*
44958  * Based on:
44959  * Ext JS Library 1.1.1
44960  * Copyright(c) 2006-2007, Ext JS, LLC.
44961  *
44962  * Originally Released Under LGPL - original licence link has changed is not relivant.
44963  *
44964  * Fork - LGPL
44965  * <script type="text/javascript">
44966  */
44967  
44968 /**
44969  * @class Roo.form.BasicForm
44970  * @extends Roo.util.Observable
44971  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44972  * @constructor
44973  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44974  * @param {Object} config Configuration options
44975  */
44976 Roo.form.BasicForm = function(el, config){
44977     this.allItems = [];
44978     this.childForms = [];
44979     Roo.apply(this, config);
44980     /*
44981      * The Roo.form.Field items in this form.
44982      * @type MixedCollection
44983      */
44984      
44985      
44986     this.items = new Roo.util.MixedCollection(false, function(o){
44987         return o.id || (o.id = Roo.id());
44988     });
44989     this.addEvents({
44990         /**
44991          * @event beforeaction
44992          * Fires before any action is performed. Return false to cancel the action.
44993          * @param {Form} this
44994          * @param {Action} action The action to be performed
44995          */
44996         beforeaction: true,
44997         /**
44998          * @event actionfailed
44999          * Fires when an action fails.
45000          * @param {Form} this
45001          * @param {Action} action The action that failed
45002          */
45003         actionfailed : true,
45004         /**
45005          * @event actioncomplete
45006          * Fires when an action is completed.
45007          * @param {Form} this
45008          * @param {Action} action The action that completed
45009          */
45010         actioncomplete : true
45011     });
45012     if(el){
45013         this.initEl(el);
45014     }
45015     Roo.form.BasicForm.superclass.constructor.call(this);
45016 };
45017
45018 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45019     /**
45020      * @cfg {String} method
45021      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45022      */
45023     /**
45024      * @cfg {DataReader} reader
45025      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45026      * This is optional as there is built-in support for processing JSON.
45027      */
45028     /**
45029      * @cfg {DataReader} errorReader
45030      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45031      * This is completely optional as there is built-in support for processing JSON.
45032      */
45033     /**
45034      * @cfg {String} url
45035      * The URL to use for form actions if one isn't supplied in the action options.
45036      */
45037     /**
45038      * @cfg {Boolean} fileUpload
45039      * Set to true if this form is a file upload.
45040      */
45041      
45042     /**
45043      * @cfg {Object} baseParams
45044      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45045      */
45046      /**
45047      
45048     /**
45049      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45050      */
45051     timeout: 30,
45052
45053     // private
45054     activeAction : null,
45055
45056     /**
45057      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45058      * or setValues() data instead of when the form was first created.
45059      */
45060     trackResetOnLoad : false,
45061     
45062     
45063     /**
45064      * childForms - used for multi-tab forms
45065      * @type {Array}
45066      */
45067     childForms : false,
45068     
45069     /**
45070      * allItems - full list of fields.
45071      * @type {Array}
45072      */
45073     allItems : false,
45074     
45075     /**
45076      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45077      * element by passing it or its id or mask the form itself by passing in true.
45078      * @type Mixed
45079      */
45080     waitMsgTarget : false,
45081
45082     // private
45083     initEl : function(el){
45084         this.el = Roo.get(el);
45085         this.id = this.el.id || Roo.id();
45086         this.el.on('submit', this.onSubmit, this);
45087         this.el.addClass('x-form');
45088     },
45089
45090     // private
45091     onSubmit : function(e){
45092         e.stopEvent();
45093     },
45094
45095     /**
45096      * Returns true if client-side validation on the form is successful.
45097      * @return Boolean
45098      */
45099     isValid : function(){
45100         var valid = true;
45101         this.items.each(function(f){
45102            if(!f.validate()){
45103                valid = false;
45104            }
45105         });
45106         return valid;
45107     },
45108
45109     /**
45110      * Returns true if any fields in this form have changed since their original load.
45111      * @return Boolean
45112      */
45113     isDirty : function(){
45114         var dirty = false;
45115         this.items.each(function(f){
45116            if(f.isDirty()){
45117                dirty = true;
45118                return false;
45119            }
45120         });
45121         return dirty;
45122     },
45123
45124     /**
45125      * Performs a predefined action (submit or load) or custom actions you define on this form.
45126      * @param {String} actionName The name of the action type
45127      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45128      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45129      * accept other config options):
45130      * <pre>
45131 Property          Type             Description
45132 ----------------  ---------------  ----------------------------------------------------------------------------------
45133 url               String           The url for the action (defaults to the form's url)
45134 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45135 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45136 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45137                                    validate the form on the client (defaults to false)
45138      * </pre>
45139      * @return {BasicForm} this
45140      */
45141     doAction : function(action, options){
45142         if(typeof action == 'string'){
45143             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45144         }
45145         if(this.fireEvent('beforeaction', this, action) !== false){
45146             this.beforeAction(action);
45147             action.run.defer(100, action);
45148         }
45149         return this;
45150     },
45151
45152     /**
45153      * Shortcut to do a submit action.
45154      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45155      * @return {BasicForm} this
45156      */
45157     submit : function(options){
45158         this.doAction('submit', options);
45159         return this;
45160     },
45161
45162     /**
45163      * Shortcut to do a load action.
45164      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45165      * @return {BasicForm} this
45166      */
45167     load : function(options){
45168         this.doAction('load', options);
45169         return this;
45170     },
45171
45172     /**
45173      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45174      * @param {Record} record The record to edit
45175      * @return {BasicForm} this
45176      */
45177     updateRecord : function(record){
45178         record.beginEdit();
45179         var fs = record.fields;
45180         fs.each(function(f){
45181             var field = this.findField(f.name);
45182             if(field){
45183                 record.set(f.name, field.getValue());
45184             }
45185         }, this);
45186         record.endEdit();
45187         return this;
45188     },
45189
45190     /**
45191      * Loads an Roo.data.Record into this form.
45192      * @param {Record} record The record to load
45193      * @return {BasicForm} this
45194      */
45195     loadRecord : function(record){
45196         this.setValues(record.data);
45197         return this;
45198     },
45199
45200     // private
45201     beforeAction : function(action){
45202         var o = action.options;
45203         
45204        
45205         if(this.waitMsgTarget === true){
45206             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45207         }else if(this.waitMsgTarget){
45208             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45209             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45210         }else {
45211             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45212         }
45213          
45214     },
45215
45216     // private
45217     afterAction : function(action, success){
45218         this.activeAction = null;
45219         var o = action.options;
45220         
45221         if(this.waitMsgTarget === true){
45222             this.el.unmask();
45223         }else if(this.waitMsgTarget){
45224             this.waitMsgTarget.unmask();
45225         }else{
45226             Roo.MessageBox.updateProgress(1);
45227             Roo.MessageBox.hide();
45228         }
45229          
45230         if(success){
45231             if(o.reset){
45232                 this.reset();
45233             }
45234             Roo.callback(o.success, o.scope, [this, action]);
45235             this.fireEvent('actioncomplete', this, action);
45236             
45237         }else{
45238             
45239             // failure condition..
45240             // we have a scenario where updates need confirming.
45241             // eg. if a locking scenario exists..
45242             // we look for { errors : { needs_confirm : true }} in the response.
45243             if (
45244                 (typeof(action.result) != 'undefined')  &&
45245                 (typeof(action.result.errors) != 'undefined')  &&
45246                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45247            ){
45248                 var _t = this;
45249                 Roo.MessageBox.confirm(
45250                     "Change requires confirmation",
45251                     action.result.errorMsg,
45252                     function(r) {
45253                         if (r != 'yes') {
45254                             return;
45255                         }
45256                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45257                     }
45258                     
45259                 );
45260                 
45261                 
45262                 
45263                 return;
45264             }
45265             
45266             Roo.callback(o.failure, o.scope, [this, action]);
45267             // show an error message if no failed handler is set..
45268             if (!this.hasListener('actionfailed')) {
45269                 Roo.MessageBox.alert("Error",
45270                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45271                         action.result.errorMsg :
45272                         "Saving Failed, please check your entries or try again"
45273                 );
45274             }
45275             
45276             this.fireEvent('actionfailed', this, action);
45277         }
45278         
45279     },
45280
45281     /**
45282      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45283      * @param {String} id The value to search for
45284      * @return Field
45285      */
45286     findField : function(id){
45287         var field = this.items.get(id);
45288         if(!field){
45289             this.items.each(function(f){
45290                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45291                     field = f;
45292                     return false;
45293                 }
45294             });
45295         }
45296         return field || null;
45297     },
45298
45299     /**
45300      * Add a secondary form to this one, 
45301      * Used to provide tabbed forms. One form is primary, with hidden values 
45302      * which mirror the elements from the other forms.
45303      * 
45304      * @param {Roo.form.Form} form to add.
45305      * 
45306      */
45307     addForm : function(form)
45308     {
45309        
45310         if (this.childForms.indexOf(form) > -1) {
45311             // already added..
45312             return;
45313         }
45314         this.childForms.push(form);
45315         var n = '';
45316         Roo.each(form.allItems, function (fe) {
45317             
45318             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45319             if (this.findField(n)) { // already added..
45320                 return;
45321             }
45322             var add = new Roo.form.Hidden({
45323                 name : n
45324             });
45325             add.render(this.el);
45326             
45327             this.add( add );
45328         }, this);
45329         
45330     },
45331     /**
45332      * Mark fields in this form invalid in bulk.
45333      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45334      * @return {BasicForm} this
45335      */
45336     markInvalid : function(errors){
45337         if(errors instanceof Array){
45338             for(var i = 0, len = errors.length; i < len; i++){
45339                 var fieldError = errors[i];
45340                 var f = this.findField(fieldError.id);
45341                 if(f){
45342                     f.markInvalid(fieldError.msg);
45343                 }
45344             }
45345         }else{
45346             var field, id;
45347             for(id in errors){
45348                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45349                     field.markInvalid(errors[id]);
45350                 }
45351             }
45352         }
45353         Roo.each(this.childForms || [], function (f) {
45354             f.markInvalid(errors);
45355         });
45356         
45357         return this;
45358     },
45359
45360     /**
45361      * Set values for fields in this form in bulk.
45362      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45363      * @return {BasicForm} this
45364      */
45365     setValues : function(values){
45366         if(values instanceof Array){ // array of objects
45367             for(var i = 0, len = values.length; i < len; i++){
45368                 var v = values[i];
45369                 var f = this.findField(v.id);
45370                 if(f){
45371                     f.setValue(v.value);
45372                     if(this.trackResetOnLoad){
45373                         f.originalValue = f.getValue();
45374                     }
45375                 }
45376             }
45377         }else{ // object hash
45378             var field, id;
45379             for(id in values){
45380                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45381                     
45382                     if (field.setFromData && 
45383                         field.valueField && 
45384                         field.displayField &&
45385                         // combos' with local stores can 
45386                         // be queried via setValue()
45387                         // to set their value..
45388                         (field.store && !field.store.isLocal)
45389                         ) {
45390                         // it's a combo
45391                         var sd = { };
45392                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45393                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45394                         field.setFromData(sd);
45395                         
45396                     } else {
45397                         field.setValue(values[id]);
45398                     }
45399                     
45400                     
45401                     if(this.trackResetOnLoad){
45402                         field.originalValue = field.getValue();
45403                     }
45404                 }
45405             }
45406         }
45407          
45408         Roo.each(this.childForms || [], function (f) {
45409             f.setValues(values);
45410         });
45411                 
45412         return this;
45413     },
45414
45415     /**
45416      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45417      * they are returned as an array.
45418      * @param {Boolean} asString
45419      * @return {Object}
45420      */
45421     getValues : function(asString){
45422         if (this.childForms) {
45423             // copy values from the child forms
45424             Roo.each(this.childForms, function (f) {
45425                 this.setValues(f.getValues());
45426             }, this);
45427         }
45428         
45429         
45430         
45431         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45432         if(asString === true){
45433             return fs;
45434         }
45435         return Roo.urlDecode(fs);
45436     },
45437     
45438     /**
45439      * Returns the fields in this form as an object with key/value pairs. 
45440      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45441      * @return {Object}
45442      */
45443     getFieldValues : function(with_hidden)
45444     {
45445         if (this.childForms) {
45446             // copy values from the child forms
45447             // should this call getFieldValues - probably not as we do not currently copy
45448             // hidden fields when we generate..
45449             Roo.each(this.childForms, function (f) {
45450                 this.setValues(f.getValues());
45451             }, this);
45452         }
45453         
45454         var ret = {};
45455         this.items.each(function(f){
45456             if (!f.getName()) {
45457                 return;
45458             }
45459             var v = f.getValue();
45460             if (f.inputType =='radio') {
45461                 if (typeof(ret[f.getName()]) == 'undefined') {
45462                     ret[f.getName()] = ''; // empty..
45463                 }
45464                 
45465                 if (!f.el.dom.checked) {
45466                     return;
45467                     
45468                 }
45469                 v = f.el.dom.value;
45470                 
45471             }
45472             
45473             // not sure if this supported any more..
45474             if ((typeof(v) == 'object') && f.getRawValue) {
45475                 v = f.getRawValue() ; // dates..
45476             }
45477             // combo boxes where name != hiddenName...
45478             if (f.name != f.getName()) {
45479                 ret[f.name] = f.getRawValue();
45480             }
45481             ret[f.getName()] = v;
45482         });
45483         
45484         return ret;
45485     },
45486
45487     /**
45488      * Clears all invalid messages in this form.
45489      * @return {BasicForm} this
45490      */
45491     clearInvalid : function(){
45492         this.items.each(function(f){
45493            f.clearInvalid();
45494         });
45495         
45496         Roo.each(this.childForms || [], function (f) {
45497             f.clearInvalid();
45498         });
45499         
45500         
45501         return this;
45502     },
45503
45504     /**
45505      * Resets this form.
45506      * @return {BasicForm} this
45507      */
45508     reset : function(){
45509         this.items.each(function(f){
45510             f.reset();
45511         });
45512         
45513         Roo.each(this.childForms || [], function (f) {
45514             f.reset();
45515         });
45516        
45517         
45518         return this;
45519     },
45520
45521     /**
45522      * Add Roo.form components to this form.
45523      * @param {Field} field1
45524      * @param {Field} field2 (optional)
45525      * @param {Field} etc (optional)
45526      * @return {BasicForm} this
45527      */
45528     add : function(){
45529         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45530         return this;
45531     },
45532
45533
45534     /**
45535      * Removes a field from the items collection (does NOT remove its markup).
45536      * @param {Field} field
45537      * @return {BasicForm} this
45538      */
45539     remove : function(field){
45540         this.items.remove(field);
45541         return this;
45542     },
45543
45544     /**
45545      * Looks at the fields in this form, checks them for an id attribute,
45546      * and calls applyTo on the existing dom element with that id.
45547      * @return {BasicForm} this
45548      */
45549     render : function(){
45550         this.items.each(function(f){
45551             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45552                 f.applyTo(f.id);
45553             }
45554         });
45555         return this;
45556     },
45557
45558     /**
45559      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45560      * @param {Object} values
45561      * @return {BasicForm} this
45562      */
45563     applyToFields : function(o){
45564         this.items.each(function(f){
45565            Roo.apply(f, o);
45566         });
45567         return this;
45568     },
45569
45570     /**
45571      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45572      * @param {Object} values
45573      * @return {BasicForm} this
45574      */
45575     applyIfToFields : function(o){
45576         this.items.each(function(f){
45577            Roo.applyIf(f, o);
45578         });
45579         return this;
45580     }
45581 });
45582
45583 // back compat
45584 Roo.BasicForm = Roo.form.BasicForm;/*
45585  * Based on:
45586  * Ext JS Library 1.1.1
45587  * Copyright(c) 2006-2007, Ext JS, LLC.
45588  *
45589  * Originally Released Under LGPL - original licence link has changed is not relivant.
45590  *
45591  * Fork - LGPL
45592  * <script type="text/javascript">
45593  */
45594
45595 /**
45596  * @class Roo.form.Form
45597  * @extends Roo.form.BasicForm
45598  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45599  * @constructor
45600  * @param {Object} config Configuration options
45601  */
45602 Roo.form.Form = function(config){
45603     var xitems =  [];
45604     if (config.items) {
45605         xitems = config.items;
45606         delete config.items;
45607     }
45608    
45609     
45610     Roo.form.Form.superclass.constructor.call(this, null, config);
45611     this.url = this.url || this.action;
45612     if(!this.root){
45613         this.root = new Roo.form.Layout(Roo.applyIf({
45614             id: Roo.id()
45615         }, config));
45616     }
45617     this.active = this.root;
45618     /**
45619      * Array of all the buttons that have been added to this form via {@link addButton}
45620      * @type Array
45621      */
45622     this.buttons = [];
45623     this.allItems = [];
45624     this.addEvents({
45625         /**
45626          * @event clientvalidation
45627          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45628          * @param {Form} this
45629          * @param {Boolean} valid true if the form has passed client-side validation
45630          */
45631         clientvalidation: true,
45632         /**
45633          * @event rendered
45634          * Fires when the form is rendered
45635          * @param {Roo.form.Form} form
45636          */
45637         rendered : true
45638     });
45639     
45640     if (this.progressUrl) {
45641             // push a hidden field onto the list of fields..
45642             this.addxtype( {
45643                     xns: Roo.form, 
45644                     xtype : 'Hidden', 
45645                     name : 'UPLOAD_IDENTIFIER' 
45646             });
45647         }
45648         
45649     
45650     Roo.each(xitems, this.addxtype, this);
45651     
45652     
45653     
45654 };
45655
45656 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45657     /**
45658      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45659      */
45660     /**
45661      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45662      */
45663     /**
45664      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45665      */
45666     buttonAlign:'center',
45667
45668     /**
45669      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45670      */
45671     minButtonWidth:75,
45672
45673     /**
45674      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45675      * This property cascades to child containers if not set.
45676      */
45677     labelAlign:'left',
45678
45679     /**
45680      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45681      * fires a looping event with that state. This is required to bind buttons to the valid
45682      * state using the config value formBind:true on the button.
45683      */
45684     monitorValid : false,
45685
45686     /**
45687      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45688      */
45689     monitorPoll : 200,
45690     
45691     /**
45692      * @cfg {String} progressUrl - Url to return progress data 
45693      */
45694     
45695     progressUrl : false,
45696   
45697     /**
45698      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45699      * fields are added and the column is closed. If no fields are passed the column remains open
45700      * until end() is called.
45701      * @param {Object} config The config to pass to the column
45702      * @param {Field} field1 (optional)
45703      * @param {Field} field2 (optional)
45704      * @param {Field} etc (optional)
45705      * @return Column The column container object
45706      */
45707     column : function(c){
45708         var col = new Roo.form.Column(c);
45709         this.start(col);
45710         if(arguments.length > 1){ // duplicate code required because of Opera
45711             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45712             this.end();
45713         }
45714         return col;
45715     },
45716
45717     /**
45718      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45719      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45720      * until end() is called.
45721      * @param {Object} config The config to pass to the fieldset
45722      * @param {Field} field1 (optional)
45723      * @param {Field} field2 (optional)
45724      * @param {Field} etc (optional)
45725      * @return FieldSet The fieldset container object
45726      */
45727     fieldset : function(c){
45728         var fs = new Roo.form.FieldSet(c);
45729         this.start(fs);
45730         if(arguments.length > 1){ // duplicate code required because of Opera
45731             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45732             this.end();
45733         }
45734         return fs;
45735     },
45736
45737     /**
45738      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45739      * fields are added and the container is closed. If no fields are passed the container remains open
45740      * until end() is called.
45741      * @param {Object} config The config to pass to the Layout
45742      * @param {Field} field1 (optional)
45743      * @param {Field} field2 (optional)
45744      * @param {Field} etc (optional)
45745      * @return Layout The container object
45746      */
45747     container : function(c){
45748         var l = new Roo.form.Layout(c);
45749         this.start(l);
45750         if(arguments.length > 1){ // duplicate code required because of Opera
45751             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45752             this.end();
45753         }
45754         return l;
45755     },
45756
45757     /**
45758      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45759      * @param {Object} container A Roo.form.Layout or subclass of Layout
45760      * @return {Form} this
45761      */
45762     start : function(c){
45763         // cascade label info
45764         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45765         this.active.stack.push(c);
45766         c.ownerCt = this.active;
45767         this.active = c;
45768         return this;
45769     },
45770
45771     /**
45772      * Closes the current open container
45773      * @return {Form} this
45774      */
45775     end : function(){
45776         if(this.active == this.root){
45777             return this;
45778         }
45779         this.active = this.active.ownerCt;
45780         return this;
45781     },
45782
45783     /**
45784      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45785      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45786      * as the label of the field.
45787      * @param {Field} field1
45788      * @param {Field} field2 (optional)
45789      * @param {Field} etc. (optional)
45790      * @return {Form} this
45791      */
45792     add : function(){
45793         this.active.stack.push.apply(this.active.stack, arguments);
45794         this.allItems.push.apply(this.allItems,arguments);
45795         var r = [];
45796         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45797             if(a[i].isFormField){
45798                 r.push(a[i]);
45799             }
45800         }
45801         if(r.length > 0){
45802             Roo.form.Form.superclass.add.apply(this, r);
45803         }
45804         return this;
45805     },
45806     
45807
45808     
45809     
45810     
45811      /**
45812      * Find any element that has been added to a form, using it's ID or name
45813      * This can include framesets, columns etc. along with regular fields..
45814      * @param {String} id - id or name to find.
45815      
45816      * @return {Element} e - or false if nothing found.
45817      */
45818     findbyId : function(id)
45819     {
45820         var ret = false;
45821         if (!id) {
45822             return ret;
45823         }
45824         Roo.each(this.allItems, function(f){
45825             if (f.id == id || f.name == id ){
45826                 ret = f;
45827                 return false;
45828             }
45829         });
45830         return ret;
45831     },
45832
45833     
45834     
45835     /**
45836      * Render this form into the passed container. This should only be called once!
45837      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45838      * @return {Form} this
45839      */
45840     render : function(ct)
45841     {
45842         
45843         
45844         
45845         ct = Roo.get(ct);
45846         var o = this.autoCreate || {
45847             tag: 'form',
45848             method : this.method || 'POST',
45849             id : this.id || Roo.id()
45850         };
45851         this.initEl(ct.createChild(o));
45852
45853         this.root.render(this.el);
45854         
45855        
45856              
45857         this.items.each(function(f){
45858             f.render('x-form-el-'+f.id);
45859         });
45860
45861         if(this.buttons.length > 0){
45862             // tables are required to maintain order and for correct IE layout
45863             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45864                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45865                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45866             }}, null, true);
45867             var tr = tb.getElementsByTagName('tr')[0];
45868             for(var i = 0, len = this.buttons.length; i < len; i++) {
45869                 var b = this.buttons[i];
45870                 var td = document.createElement('td');
45871                 td.className = 'x-form-btn-td';
45872                 b.render(tr.appendChild(td));
45873             }
45874         }
45875         if(this.monitorValid){ // initialize after render
45876             this.startMonitoring();
45877         }
45878         this.fireEvent('rendered', this);
45879         return this;
45880     },
45881
45882     /**
45883      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45884      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45885      * object or a valid Roo.DomHelper element config
45886      * @param {Function} handler The function called when the button is clicked
45887      * @param {Object} scope (optional) The scope of the handler function
45888      * @return {Roo.Button}
45889      */
45890     addButton : function(config, handler, scope){
45891         var bc = {
45892             handler: handler,
45893             scope: scope,
45894             minWidth: this.minButtonWidth,
45895             hideParent:true
45896         };
45897         if(typeof config == "string"){
45898             bc.text = config;
45899         }else{
45900             Roo.apply(bc, config);
45901         }
45902         var btn = new Roo.Button(null, bc);
45903         this.buttons.push(btn);
45904         return btn;
45905     },
45906
45907      /**
45908      * Adds a series of form elements (using the xtype property as the factory method.
45909      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45910      * @param {Object} config 
45911      */
45912     
45913     addxtype : function()
45914     {
45915         var ar = Array.prototype.slice.call(arguments, 0);
45916         var ret = false;
45917         for(var i = 0; i < ar.length; i++) {
45918             if (!ar[i]) {
45919                 continue; // skip -- if this happends something invalid got sent, we 
45920                 // should ignore it, as basically that interface element will not show up
45921                 // and that should be pretty obvious!!
45922             }
45923             
45924             if (Roo.form[ar[i].xtype]) {
45925                 ar[i].form = this;
45926                 var fe = Roo.factory(ar[i], Roo.form);
45927                 if (!ret) {
45928                     ret = fe;
45929                 }
45930                 fe.form = this;
45931                 if (fe.store) {
45932                     fe.store.form = this;
45933                 }
45934                 if (fe.isLayout) {  
45935                          
45936                     this.start(fe);
45937                     this.allItems.push(fe);
45938                     if (fe.items && fe.addxtype) {
45939                         fe.addxtype.apply(fe, fe.items);
45940                         delete fe.items;
45941                     }
45942                      this.end();
45943                     continue;
45944                 }
45945                 
45946                 
45947                  
45948                 this.add(fe);
45949               //  console.log('adding ' + ar[i].xtype);
45950             }
45951             if (ar[i].xtype == 'Button') {  
45952                 //console.log('adding button');
45953                 //console.log(ar[i]);
45954                 this.addButton(ar[i]);
45955                 this.allItems.push(fe);
45956                 continue;
45957             }
45958             
45959             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45960                 alert('end is not supported on xtype any more, use items');
45961             //    this.end();
45962             //    //console.log('adding end');
45963             }
45964             
45965         }
45966         return ret;
45967     },
45968     
45969     /**
45970      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45971      * option "monitorValid"
45972      */
45973     startMonitoring : function(){
45974         if(!this.bound){
45975             this.bound = true;
45976             Roo.TaskMgr.start({
45977                 run : this.bindHandler,
45978                 interval : this.monitorPoll || 200,
45979                 scope: this
45980             });
45981         }
45982     },
45983
45984     /**
45985      * Stops monitoring of the valid state of this form
45986      */
45987     stopMonitoring : function(){
45988         this.bound = false;
45989     },
45990
45991     // private
45992     bindHandler : function(){
45993         if(!this.bound){
45994             return false; // stops binding
45995         }
45996         var valid = true;
45997         this.items.each(function(f){
45998             if(!f.isValid(true)){
45999                 valid = false;
46000                 return false;
46001             }
46002         });
46003         for(var i = 0, len = this.buttons.length; i < len; i++){
46004             var btn = this.buttons[i];
46005             if(btn.formBind === true && btn.disabled === valid){
46006                 btn.setDisabled(!valid);
46007             }
46008         }
46009         this.fireEvent('clientvalidation', this, valid);
46010     }
46011     
46012     
46013     
46014     
46015     
46016     
46017     
46018     
46019 });
46020
46021
46022 // back compat
46023 Roo.Form = Roo.form.Form;
46024 /*
46025  * Based on:
46026  * Ext JS Library 1.1.1
46027  * Copyright(c) 2006-2007, Ext JS, LLC.
46028  *
46029  * Originally Released Under LGPL - original licence link has changed is not relivant.
46030  *
46031  * Fork - LGPL
46032  * <script type="text/javascript">
46033  */
46034
46035 // as we use this in bootstrap.
46036 Roo.namespace('Roo.form');
46037  /**
46038  * @class Roo.form.Action
46039  * Internal Class used to handle form actions
46040  * @constructor
46041  * @param {Roo.form.BasicForm} el The form element or its id
46042  * @param {Object} config Configuration options
46043  */
46044
46045  
46046  
46047 // define the action interface
46048 Roo.form.Action = function(form, options){
46049     this.form = form;
46050     this.options = options || {};
46051 };
46052 /**
46053  * Client Validation Failed
46054  * @const 
46055  */
46056 Roo.form.Action.CLIENT_INVALID = 'client';
46057 /**
46058  * Server Validation Failed
46059  * @const 
46060  */
46061 Roo.form.Action.SERVER_INVALID = 'server';
46062  /**
46063  * Connect to Server Failed
46064  * @const 
46065  */
46066 Roo.form.Action.CONNECT_FAILURE = 'connect';
46067 /**
46068  * Reading Data from Server Failed
46069  * @const 
46070  */
46071 Roo.form.Action.LOAD_FAILURE = 'load';
46072
46073 Roo.form.Action.prototype = {
46074     type : 'default',
46075     failureType : undefined,
46076     response : undefined,
46077     result : undefined,
46078
46079     // interface method
46080     run : function(options){
46081
46082     },
46083
46084     // interface method
46085     success : function(response){
46086
46087     },
46088
46089     // interface method
46090     handleResponse : function(response){
46091
46092     },
46093
46094     // default connection failure
46095     failure : function(response){
46096         
46097         this.response = response;
46098         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46099         this.form.afterAction(this, false);
46100     },
46101
46102     processResponse : function(response){
46103         this.response = response;
46104         if(!response.responseText){
46105             return true;
46106         }
46107         this.result = this.handleResponse(response);
46108         return this.result;
46109     },
46110
46111     // utility functions used internally
46112     getUrl : function(appendParams){
46113         var url = this.options.url || this.form.url || this.form.el.dom.action;
46114         if(appendParams){
46115             var p = this.getParams();
46116             if(p){
46117                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46118             }
46119         }
46120         return url;
46121     },
46122
46123     getMethod : function(){
46124         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46125     },
46126
46127     getParams : function(){
46128         var bp = this.form.baseParams;
46129         var p = this.options.params;
46130         if(p){
46131             if(typeof p == "object"){
46132                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46133             }else if(typeof p == 'string' && bp){
46134                 p += '&' + Roo.urlEncode(bp);
46135             }
46136         }else if(bp){
46137             p = Roo.urlEncode(bp);
46138         }
46139         return p;
46140     },
46141
46142     createCallback : function(){
46143         return {
46144             success: this.success,
46145             failure: this.failure,
46146             scope: this,
46147             timeout: (this.form.timeout*1000),
46148             upload: this.form.fileUpload ? this.success : undefined
46149         };
46150     }
46151 };
46152
46153 Roo.form.Action.Submit = function(form, options){
46154     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46155 };
46156
46157 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46158     type : 'submit',
46159
46160     haveProgress : false,
46161     uploadComplete : false,
46162     
46163     // uploadProgress indicator.
46164     uploadProgress : function()
46165     {
46166         if (!this.form.progressUrl) {
46167             return;
46168         }
46169         
46170         if (!this.haveProgress) {
46171             Roo.MessageBox.progress("Uploading", "Uploading");
46172         }
46173         if (this.uploadComplete) {
46174            Roo.MessageBox.hide();
46175            return;
46176         }
46177         
46178         this.haveProgress = true;
46179    
46180         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46181         
46182         var c = new Roo.data.Connection();
46183         c.request({
46184             url : this.form.progressUrl,
46185             params: {
46186                 id : uid
46187             },
46188             method: 'GET',
46189             success : function(req){
46190                //console.log(data);
46191                 var rdata = false;
46192                 var edata;
46193                 try  {
46194                    rdata = Roo.decode(req.responseText)
46195                 } catch (e) {
46196                     Roo.log("Invalid data from server..");
46197                     Roo.log(edata);
46198                     return;
46199                 }
46200                 if (!rdata || !rdata.success) {
46201                     Roo.log(rdata);
46202                     Roo.MessageBox.alert(Roo.encode(rdata));
46203                     return;
46204                 }
46205                 var data = rdata.data;
46206                 
46207                 if (this.uploadComplete) {
46208                    Roo.MessageBox.hide();
46209                    return;
46210                 }
46211                    
46212                 if (data){
46213                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46214                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46215                     );
46216                 }
46217                 this.uploadProgress.defer(2000,this);
46218             },
46219        
46220             failure: function(data) {
46221                 Roo.log('progress url failed ');
46222                 Roo.log(data);
46223             },
46224             scope : this
46225         });
46226            
46227     },
46228     
46229     
46230     run : function()
46231     {
46232         // run get Values on the form, so it syncs any secondary forms.
46233         this.form.getValues();
46234         
46235         var o = this.options;
46236         var method = this.getMethod();
46237         var isPost = method == 'POST';
46238         if(o.clientValidation === false || this.form.isValid()){
46239             
46240             if (this.form.progressUrl) {
46241                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46242                     (new Date() * 1) + '' + Math.random());
46243                     
46244             } 
46245             
46246             
46247             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46248                 form:this.form.el.dom,
46249                 url:this.getUrl(!isPost),
46250                 method: method,
46251                 params:isPost ? this.getParams() : null,
46252                 isUpload: this.form.fileUpload
46253             }));
46254             
46255             this.uploadProgress();
46256
46257         }else if (o.clientValidation !== false){ // client validation failed
46258             this.failureType = Roo.form.Action.CLIENT_INVALID;
46259             this.form.afterAction(this, false);
46260         }
46261     },
46262
46263     success : function(response)
46264     {
46265         this.uploadComplete= true;
46266         if (this.haveProgress) {
46267             Roo.MessageBox.hide();
46268         }
46269         
46270         
46271         var result = this.processResponse(response);
46272         if(result === true || result.success){
46273             this.form.afterAction(this, true);
46274             return;
46275         }
46276         if(result.errors){
46277             this.form.markInvalid(result.errors);
46278             this.failureType = Roo.form.Action.SERVER_INVALID;
46279         }
46280         this.form.afterAction(this, false);
46281     },
46282     failure : function(response)
46283     {
46284         this.uploadComplete= true;
46285         if (this.haveProgress) {
46286             Roo.MessageBox.hide();
46287         }
46288         
46289         this.response = response;
46290         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46291         this.form.afterAction(this, false);
46292     },
46293     
46294     handleResponse : function(response){
46295         if(this.form.errorReader){
46296             var rs = this.form.errorReader.read(response);
46297             var errors = [];
46298             if(rs.records){
46299                 for(var i = 0, len = rs.records.length; i < len; i++) {
46300                     var r = rs.records[i];
46301                     errors[i] = r.data;
46302                 }
46303             }
46304             if(errors.length < 1){
46305                 errors = null;
46306             }
46307             return {
46308                 success : rs.success,
46309                 errors : errors
46310             };
46311         }
46312         var ret = false;
46313         try {
46314             ret = Roo.decode(response.responseText);
46315         } catch (e) {
46316             ret = {
46317                 success: false,
46318                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46319                 errors : []
46320             };
46321         }
46322         return ret;
46323         
46324     }
46325 });
46326
46327
46328 Roo.form.Action.Load = function(form, options){
46329     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46330     this.reader = this.form.reader;
46331 };
46332
46333 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46334     type : 'load',
46335
46336     run : function(){
46337         
46338         Roo.Ajax.request(Roo.apply(
46339                 this.createCallback(), {
46340                     method:this.getMethod(),
46341                     url:this.getUrl(false),
46342                     params:this.getParams()
46343         }));
46344     },
46345
46346     success : function(response){
46347         
46348         var result = this.processResponse(response);
46349         if(result === true || !result.success || !result.data){
46350             this.failureType = Roo.form.Action.LOAD_FAILURE;
46351             this.form.afterAction(this, false);
46352             return;
46353         }
46354         this.form.clearInvalid();
46355         this.form.setValues(result.data);
46356         this.form.afterAction(this, true);
46357     },
46358
46359     handleResponse : function(response){
46360         if(this.form.reader){
46361             var rs = this.form.reader.read(response);
46362             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46363             return {
46364                 success : rs.success,
46365                 data : data
46366             };
46367         }
46368         return Roo.decode(response.responseText);
46369     }
46370 });
46371
46372 Roo.form.Action.ACTION_TYPES = {
46373     'load' : Roo.form.Action.Load,
46374     'submit' : Roo.form.Action.Submit
46375 };/*
46376  * Based on:
46377  * Ext JS Library 1.1.1
46378  * Copyright(c) 2006-2007, Ext JS, LLC.
46379  *
46380  * Originally Released Under LGPL - original licence link has changed is not relivant.
46381  *
46382  * Fork - LGPL
46383  * <script type="text/javascript">
46384  */
46385  
46386 /**
46387  * @class Roo.form.Layout
46388  * @extends Roo.Component
46389  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46390  * @constructor
46391  * @param {Object} config Configuration options
46392  */
46393 Roo.form.Layout = function(config){
46394     var xitems = [];
46395     if (config.items) {
46396         xitems = config.items;
46397         delete config.items;
46398     }
46399     Roo.form.Layout.superclass.constructor.call(this, config);
46400     this.stack = [];
46401     Roo.each(xitems, this.addxtype, this);
46402      
46403 };
46404
46405 Roo.extend(Roo.form.Layout, Roo.Component, {
46406     /**
46407      * @cfg {String/Object} autoCreate
46408      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46409      */
46410     /**
46411      * @cfg {String/Object/Function} style
46412      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46413      * a function which returns such a specification.
46414      */
46415     /**
46416      * @cfg {String} labelAlign
46417      * Valid values are "left," "top" and "right" (defaults to "left")
46418      */
46419     /**
46420      * @cfg {Number} labelWidth
46421      * Fixed width in pixels of all field labels (defaults to undefined)
46422      */
46423     /**
46424      * @cfg {Boolean} clear
46425      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46426      */
46427     clear : true,
46428     /**
46429      * @cfg {String} labelSeparator
46430      * The separator to use after field labels (defaults to ':')
46431      */
46432     labelSeparator : ':',
46433     /**
46434      * @cfg {Boolean} hideLabels
46435      * True to suppress the display of field labels in this layout (defaults to false)
46436      */
46437     hideLabels : false,
46438
46439     // private
46440     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46441     
46442     isLayout : true,
46443     
46444     // private
46445     onRender : function(ct, position){
46446         if(this.el){ // from markup
46447             this.el = Roo.get(this.el);
46448         }else {  // generate
46449             var cfg = this.getAutoCreate();
46450             this.el = ct.createChild(cfg, position);
46451         }
46452         if(this.style){
46453             this.el.applyStyles(this.style);
46454         }
46455         if(this.labelAlign){
46456             this.el.addClass('x-form-label-'+this.labelAlign);
46457         }
46458         if(this.hideLabels){
46459             this.labelStyle = "display:none";
46460             this.elementStyle = "padding-left:0;";
46461         }else{
46462             if(typeof this.labelWidth == 'number'){
46463                 this.labelStyle = "width:"+this.labelWidth+"px;";
46464                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46465             }
46466             if(this.labelAlign == 'top'){
46467                 this.labelStyle = "width:auto;";
46468                 this.elementStyle = "padding-left:0;";
46469             }
46470         }
46471         var stack = this.stack;
46472         var slen = stack.length;
46473         if(slen > 0){
46474             if(!this.fieldTpl){
46475                 var t = new Roo.Template(
46476                     '<div class="x-form-item {5}">',
46477                         '<label for="{0}" style="{2}">{1}{4}</label>',
46478                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46479                         '</div>',
46480                     '</div><div class="x-form-clear-left"></div>'
46481                 );
46482                 t.disableFormats = true;
46483                 t.compile();
46484                 Roo.form.Layout.prototype.fieldTpl = t;
46485             }
46486             for(var i = 0; i < slen; i++) {
46487                 if(stack[i].isFormField){
46488                     this.renderField(stack[i]);
46489                 }else{
46490                     this.renderComponent(stack[i]);
46491                 }
46492             }
46493         }
46494         if(this.clear){
46495             this.el.createChild({cls:'x-form-clear'});
46496         }
46497     },
46498
46499     // private
46500     renderField : function(f){
46501         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46502                f.id, //0
46503                f.fieldLabel, //1
46504                f.labelStyle||this.labelStyle||'', //2
46505                this.elementStyle||'', //3
46506                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46507                f.itemCls||this.itemCls||''  //5
46508        ], true).getPrevSibling());
46509     },
46510
46511     // private
46512     renderComponent : function(c){
46513         c.render(c.isLayout ? this.el : this.el.createChild());    
46514     },
46515     /**
46516      * Adds a object form elements (using the xtype property as the factory method.)
46517      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46518      * @param {Object} config 
46519      */
46520     addxtype : function(o)
46521     {
46522         // create the lement.
46523         o.form = this.form;
46524         var fe = Roo.factory(o, Roo.form);
46525         this.form.allItems.push(fe);
46526         this.stack.push(fe);
46527         
46528         if (fe.isFormField) {
46529             this.form.items.add(fe);
46530         }
46531          
46532         return fe;
46533     }
46534 });
46535
46536 /**
46537  * @class Roo.form.Column
46538  * @extends Roo.form.Layout
46539  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46540  * @constructor
46541  * @param {Object} config Configuration options
46542  */
46543 Roo.form.Column = function(config){
46544     Roo.form.Column.superclass.constructor.call(this, config);
46545 };
46546
46547 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46548     /**
46549      * @cfg {Number/String} width
46550      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46551      */
46552     /**
46553      * @cfg {String/Object} autoCreate
46554      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46555      */
46556
46557     // private
46558     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46559
46560     // private
46561     onRender : function(ct, position){
46562         Roo.form.Column.superclass.onRender.call(this, ct, position);
46563         if(this.width){
46564             this.el.setWidth(this.width);
46565         }
46566     }
46567 });
46568
46569
46570 /**
46571  * @class Roo.form.Row
46572  * @extends Roo.form.Layout
46573  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46574  * @constructor
46575  * @param {Object} config Configuration options
46576  */
46577
46578  
46579 Roo.form.Row = function(config){
46580     Roo.form.Row.superclass.constructor.call(this, config);
46581 };
46582  
46583 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46584       /**
46585      * @cfg {Number/String} width
46586      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46587      */
46588     /**
46589      * @cfg {Number/String} height
46590      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46591      */
46592     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46593     
46594     padWidth : 20,
46595     // private
46596     onRender : function(ct, position){
46597         //console.log('row render');
46598         if(!this.rowTpl){
46599             var t = new Roo.Template(
46600                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46601                     '<label for="{0}" style="{2}">{1}{4}</label>',
46602                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46603                     '</div>',
46604                 '</div>'
46605             );
46606             t.disableFormats = true;
46607             t.compile();
46608             Roo.form.Layout.prototype.rowTpl = t;
46609         }
46610         this.fieldTpl = this.rowTpl;
46611         
46612         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46613         var labelWidth = 100;
46614         
46615         if ((this.labelAlign != 'top')) {
46616             if (typeof this.labelWidth == 'number') {
46617                 labelWidth = this.labelWidth
46618             }
46619             this.padWidth =  20 + labelWidth;
46620             
46621         }
46622         
46623         Roo.form.Column.superclass.onRender.call(this, ct, position);
46624         if(this.width){
46625             this.el.setWidth(this.width);
46626         }
46627         if(this.height){
46628             this.el.setHeight(this.height);
46629         }
46630     },
46631     
46632     // private
46633     renderField : function(f){
46634         f.fieldEl = this.fieldTpl.append(this.el, [
46635                f.id, f.fieldLabel,
46636                f.labelStyle||this.labelStyle||'',
46637                this.elementStyle||'',
46638                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46639                f.itemCls||this.itemCls||'',
46640                f.width ? f.width + this.padWidth : 160 + this.padWidth
46641        ],true);
46642     }
46643 });
46644  
46645
46646 /**
46647  * @class Roo.form.FieldSet
46648  * @extends Roo.form.Layout
46649  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46650  * @constructor
46651  * @param {Object} config Configuration options
46652  */
46653 Roo.form.FieldSet = function(config){
46654     Roo.form.FieldSet.superclass.constructor.call(this, config);
46655 };
46656
46657 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46658     /**
46659      * @cfg {String} legend
46660      * The text to display as the legend for the FieldSet (defaults to '')
46661      */
46662     /**
46663      * @cfg {String/Object} autoCreate
46664      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46665      */
46666
46667     // private
46668     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46669
46670     // private
46671     onRender : function(ct, position){
46672         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46673         if(this.legend){
46674             this.setLegend(this.legend);
46675         }
46676     },
46677
46678     // private
46679     setLegend : function(text){
46680         if(this.rendered){
46681             this.el.child('legend').update(text);
46682         }
46683     }
46684 });/*
46685  * Based on:
46686  * Ext JS Library 1.1.1
46687  * Copyright(c) 2006-2007, Ext JS, LLC.
46688  *
46689  * Originally Released Under LGPL - original licence link has changed is not relivant.
46690  *
46691  * Fork - LGPL
46692  * <script type="text/javascript">
46693  */
46694 /**
46695  * @class Roo.form.VTypes
46696  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46697  * @singleton
46698  */
46699 Roo.form.VTypes = function(){
46700     // closure these in so they are only created once.
46701     var alpha = /^[a-zA-Z_]+$/;
46702     var alphanum = /^[a-zA-Z0-9_]+$/;
46703     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46704     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46705
46706     // All these messages and functions are configurable
46707     return {
46708         /**
46709          * The function used to validate email addresses
46710          * @param {String} value The email address
46711          */
46712         'email' : function(v){
46713             return email.test(v);
46714         },
46715         /**
46716          * The error text to display when the email validation function returns false
46717          * @type String
46718          */
46719         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46720         /**
46721          * The keystroke filter mask to be applied on email input
46722          * @type RegExp
46723          */
46724         'emailMask' : /[a-z0-9_\.\-@]/i,
46725
46726         /**
46727          * The function used to validate URLs
46728          * @param {String} value The URL
46729          */
46730         'url' : function(v){
46731             return url.test(v);
46732         },
46733         /**
46734          * The error text to display when the url validation function returns false
46735          * @type String
46736          */
46737         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46738         
46739         /**
46740          * The function used to validate alpha values
46741          * @param {String} value The value
46742          */
46743         'alpha' : function(v){
46744             return alpha.test(v);
46745         },
46746         /**
46747          * The error text to display when the alpha validation function returns false
46748          * @type String
46749          */
46750         'alphaText' : 'This field should only contain letters and _',
46751         /**
46752          * The keystroke filter mask to be applied on alpha input
46753          * @type RegExp
46754          */
46755         'alphaMask' : /[a-z_]/i,
46756
46757         /**
46758          * The function used to validate alphanumeric values
46759          * @param {String} value The value
46760          */
46761         'alphanum' : function(v){
46762             return alphanum.test(v);
46763         },
46764         /**
46765          * The error text to display when the alphanumeric validation function returns false
46766          * @type String
46767          */
46768         'alphanumText' : 'This field should only contain letters, numbers and _',
46769         /**
46770          * The keystroke filter mask to be applied on alphanumeric input
46771          * @type RegExp
46772          */
46773         'alphanumMask' : /[a-z0-9_]/i
46774     };
46775 }();//<script type="text/javascript">
46776
46777 /**
46778  * @class Roo.form.FCKeditor
46779  * @extends Roo.form.TextArea
46780  * Wrapper around the FCKEditor http://www.fckeditor.net
46781  * @constructor
46782  * Creates a new FCKeditor
46783  * @param {Object} config Configuration options
46784  */
46785 Roo.form.FCKeditor = function(config){
46786     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46787     this.addEvents({
46788          /**
46789          * @event editorinit
46790          * Fired when the editor is initialized - you can add extra handlers here..
46791          * @param {FCKeditor} this
46792          * @param {Object} the FCK object.
46793          */
46794         editorinit : true
46795     });
46796     
46797     
46798 };
46799 Roo.form.FCKeditor.editors = { };
46800 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46801 {
46802     //defaultAutoCreate : {
46803     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46804     //},
46805     // private
46806     /**
46807      * @cfg {Object} fck options - see fck manual for details.
46808      */
46809     fckconfig : false,
46810     
46811     /**
46812      * @cfg {Object} fck toolbar set (Basic or Default)
46813      */
46814     toolbarSet : 'Basic',
46815     /**
46816      * @cfg {Object} fck BasePath
46817      */ 
46818     basePath : '/fckeditor/',
46819     
46820     
46821     frame : false,
46822     
46823     value : '',
46824     
46825    
46826     onRender : function(ct, position)
46827     {
46828         if(!this.el){
46829             this.defaultAutoCreate = {
46830                 tag: "textarea",
46831                 style:"width:300px;height:60px;",
46832                 autocomplete: "new-password"
46833             };
46834         }
46835         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46836         /*
46837         if(this.grow){
46838             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46839             if(this.preventScrollbars){
46840                 this.el.setStyle("overflow", "hidden");
46841             }
46842             this.el.setHeight(this.growMin);
46843         }
46844         */
46845         //console.log('onrender' + this.getId() );
46846         Roo.form.FCKeditor.editors[this.getId()] = this;
46847          
46848
46849         this.replaceTextarea() ;
46850         
46851     },
46852     
46853     getEditor : function() {
46854         return this.fckEditor;
46855     },
46856     /**
46857      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46858      * @param {Mixed} value The value to set
46859      */
46860     
46861     
46862     setValue : function(value)
46863     {
46864         //console.log('setValue: ' + value);
46865         
46866         if(typeof(value) == 'undefined') { // not sure why this is happending...
46867             return;
46868         }
46869         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46870         
46871         //if(!this.el || !this.getEditor()) {
46872         //    this.value = value;
46873             //this.setValue.defer(100,this,[value]);    
46874         //    return;
46875         //} 
46876         
46877         if(!this.getEditor()) {
46878             return;
46879         }
46880         
46881         this.getEditor().SetData(value);
46882         
46883         //
46884
46885     },
46886
46887     /**
46888      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46889      * @return {Mixed} value The field value
46890      */
46891     getValue : function()
46892     {
46893         
46894         if (this.frame && this.frame.dom.style.display == 'none') {
46895             return Roo.form.FCKeditor.superclass.getValue.call(this);
46896         }
46897         
46898         if(!this.el || !this.getEditor()) {
46899            
46900            // this.getValue.defer(100,this); 
46901             return this.value;
46902         }
46903        
46904         
46905         var value=this.getEditor().GetData();
46906         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46907         return Roo.form.FCKeditor.superclass.getValue.call(this);
46908         
46909
46910     },
46911
46912     /**
46913      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46914      * @return {Mixed} value The field value
46915      */
46916     getRawValue : function()
46917     {
46918         if (this.frame && this.frame.dom.style.display == 'none') {
46919             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46920         }
46921         
46922         if(!this.el || !this.getEditor()) {
46923             //this.getRawValue.defer(100,this); 
46924             return this.value;
46925             return;
46926         }
46927         
46928         
46929         
46930         var value=this.getEditor().GetData();
46931         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46932         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46933          
46934     },
46935     
46936     setSize : function(w,h) {
46937         
46938         
46939         
46940         //if (this.frame && this.frame.dom.style.display == 'none') {
46941         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46942         //    return;
46943         //}
46944         //if(!this.el || !this.getEditor()) {
46945         //    this.setSize.defer(100,this, [w,h]); 
46946         //    return;
46947         //}
46948         
46949         
46950         
46951         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46952         
46953         this.frame.dom.setAttribute('width', w);
46954         this.frame.dom.setAttribute('height', h);
46955         this.frame.setSize(w,h);
46956         
46957     },
46958     
46959     toggleSourceEdit : function(value) {
46960         
46961       
46962          
46963         this.el.dom.style.display = value ? '' : 'none';
46964         this.frame.dom.style.display = value ?  'none' : '';
46965         
46966     },
46967     
46968     
46969     focus: function(tag)
46970     {
46971         if (this.frame.dom.style.display == 'none') {
46972             return Roo.form.FCKeditor.superclass.focus.call(this);
46973         }
46974         if(!this.el || !this.getEditor()) {
46975             this.focus.defer(100,this, [tag]); 
46976             return;
46977         }
46978         
46979         
46980         
46981         
46982         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46983         this.getEditor().Focus();
46984         if (tgs.length) {
46985             if (!this.getEditor().Selection.GetSelection()) {
46986                 this.focus.defer(100,this, [tag]); 
46987                 return;
46988             }
46989             
46990             
46991             var r = this.getEditor().EditorDocument.createRange();
46992             r.setStart(tgs[0],0);
46993             r.setEnd(tgs[0],0);
46994             this.getEditor().Selection.GetSelection().removeAllRanges();
46995             this.getEditor().Selection.GetSelection().addRange(r);
46996             this.getEditor().Focus();
46997         }
46998         
46999     },
47000     
47001     
47002     
47003     replaceTextarea : function()
47004     {
47005         if ( document.getElementById( this.getId() + '___Frame' ) )
47006             return ;
47007         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
47008         //{
47009             // We must check the elements firstly using the Id and then the name.
47010         var oTextarea = document.getElementById( this.getId() );
47011         
47012         var colElementsByName = document.getElementsByName( this.getId() ) ;
47013          
47014         oTextarea.style.display = 'none' ;
47015
47016         if ( oTextarea.tabIndex ) {            
47017             this.TabIndex = oTextarea.tabIndex ;
47018         }
47019         
47020         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47021         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47022         this.frame = Roo.get(this.getId() + '___Frame')
47023     },
47024     
47025     _getConfigHtml : function()
47026     {
47027         var sConfig = '' ;
47028
47029         for ( var o in this.fckconfig ) {
47030             sConfig += sConfig.length > 0  ? '&amp;' : '';
47031             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47032         }
47033
47034         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47035     },
47036     
47037     
47038     _getIFrameHtml : function()
47039     {
47040         var sFile = 'fckeditor.html' ;
47041         /* no idea what this is about..
47042         try
47043         {
47044             if ( (/fcksource=true/i).test( window.top.location.search ) )
47045                 sFile = 'fckeditor.original.html' ;
47046         }
47047         catch (e) { 
47048         */
47049
47050         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47051         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47052         
47053         
47054         var html = '<iframe id="' + this.getId() +
47055             '___Frame" src="' + sLink +
47056             '" width="' + this.width +
47057             '" height="' + this.height + '"' +
47058             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47059             ' frameborder="0" scrolling="no"></iframe>' ;
47060
47061         return html ;
47062     },
47063     
47064     _insertHtmlBefore : function( html, element )
47065     {
47066         if ( element.insertAdjacentHTML )       {
47067             // IE
47068             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47069         } else { // Gecko
47070             var oRange = document.createRange() ;
47071             oRange.setStartBefore( element ) ;
47072             var oFragment = oRange.createContextualFragment( html );
47073             element.parentNode.insertBefore( oFragment, element ) ;
47074         }
47075     }
47076     
47077     
47078   
47079     
47080     
47081     
47082     
47083
47084 });
47085
47086 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47087
47088 function FCKeditor_OnComplete(editorInstance){
47089     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47090     f.fckEditor = editorInstance;
47091     //console.log("loaded");
47092     f.fireEvent('editorinit', f, editorInstance);
47093
47094   
47095
47096  
47097
47098
47099
47100
47101
47102
47103
47104
47105
47106
47107
47108
47109
47110
47111
47112 //<script type="text/javascript">
47113 /**
47114  * @class Roo.form.GridField
47115  * @extends Roo.form.Field
47116  * Embed a grid (or editable grid into a form)
47117  * STATUS ALPHA
47118  * 
47119  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47120  * it needs 
47121  * xgrid.store = Roo.data.Store
47122  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47123  * xgrid.store.reader = Roo.data.JsonReader 
47124  * 
47125  * 
47126  * @constructor
47127  * Creates a new GridField
47128  * @param {Object} config Configuration options
47129  */
47130 Roo.form.GridField = function(config){
47131     Roo.form.GridField.superclass.constructor.call(this, config);
47132      
47133 };
47134
47135 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47136     /**
47137      * @cfg {Number} width  - used to restrict width of grid..
47138      */
47139     width : 100,
47140     /**
47141      * @cfg {Number} height - used to restrict height of grid..
47142      */
47143     height : 50,
47144      /**
47145      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47146          * 
47147          *}
47148      */
47149     xgrid : false, 
47150     /**
47151      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47152      * {tag: "input", type: "checkbox", autocomplete: "off"})
47153      */
47154    // defaultAutoCreate : { tag: 'div' },
47155     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47156     /**
47157      * @cfg {String} addTitle Text to include for adding a title.
47158      */
47159     addTitle : false,
47160     //
47161     onResize : function(){
47162         Roo.form.Field.superclass.onResize.apply(this, arguments);
47163     },
47164
47165     initEvents : function(){
47166         // Roo.form.Checkbox.superclass.initEvents.call(this);
47167         // has no events...
47168        
47169     },
47170
47171
47172     getResizeEl : function(){
47173         return this.wrap;
47174     },
47175
47176     getPositionEl : function(){
47177         return this.wrap;
47178     },
47179
47180     // private
47181     onRender : function(ct, position){
47182         
47183         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47184         var style = this.style;
47185         delete this.style;
47186         
47187         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47188         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47189         this.viewEl = this.wrap.createChild({ tag: 'div' });
47190         if (style) {
47191             this.viewEl.applyStyles(style);
47192         }
47193         if (this.width) {
47194             this.viewEl.setWidth(this.width);
47195         }
47196         if (this.height) {
47197             this.viewEl.setHeight(this.height);
47198         }
47199         //if(this.inputValue !== undefined){
47200         //this.setValue(this.value);
47201         
47202         
47203         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47204         
47205         
47206         this.grid.render();
47207         this.grid.getDataSource().on('remove', this.refreshValue, this);
47208         this.grid.getDataSource().on('update', this.refreshValue, this);
47209         this.grid.on('afteredit', this.refreshValue, this);
47210  
47211     },
47212      
47213     
47214     /**
47215      * Sets the value of the item. 
47216      * @param {String} either an object  or a string..
47217      */
47218     setValue : function(v){
47219         //this.value = v;
47220         v = v || []; // empty set..
47221         // this does not seem smart - it really only affects memoryproxy grids..
47222         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47223             var ds = this.grid.getDataSource();
47224             // assumes a json reader..
47225             var data = {}
47226             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47227             ds.loadData( data);
47228         }
47229         // clear selection so it does not get stale.
47230         if (this.grid.sm) { 
47231             this.grid.sm.clearSelections();
47232         }
47233         
47234         Roo.form.GridField.superclass.setValue.call(this, v);
47235         this.refreshValue();
47236         // should load data in the grid really....
47237     },
47238     
47239     // private
47240     refreshValue: function() {
47241          var val = [];
47242         this.grid.getDataSource().each(function(r) {
47243             val.push(r.data);
47244         });
47245         this.el.dom.value = Roo.encode(val);
47246     }
47247     
47248      
47249     
47250     
47251 });/*
47252  * Based on:
47253  * Ext JS Library 1.1.1
47254  * Copyright(c) 2006-2007, Ext JS, LLC.
47255  *
47256  * Originally Released Under LGPL - original licence link has changed is not relivant.
47257  *
47258  * Fork - LGPL
47259  * <script type="text/javascript">
47260  */
47261 /**
47262  * @class Roo.form.DisplayField
47263  * @extends Roo.form.Field
47264  * A generic Field to display non-editable data.
47265  * @constructor
47266  * Creates a new Display Field item.
47267  * @param {Object} config Configuration options
47268  */
47269 Roo.form.DisplayField = function(config){
47270     Roo.form.DisplayField.superclass.constructor.call(this, config);
47271     
47272 };
47273
47274 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47275     inputType:      'hidden',
47276     allowBlank:     true,
47277     readOnly:         true,
47278     
47279  
47280     /**
47281      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47282      */
47283     focusClass : undefined,
47284     /**
47285      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47286      */
47287     fieldClass: 'x-form-field',
47288     
47289      /**
47290      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47291      */
47292     valueRenderer: undefined,
47293     
47294     width: 100,
47295     /**
47296      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47297      * {tag: "input", type: "checkbox", autocomplete: "off"})
47298      */
47299      
47300  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47301
47302     onResize : function(){
47303         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47304         
47305     },
47306
47307     initEvents : function(){
47308         // Roo.form.Checkbox.superclass.initEvents.call(this);
47309         // has no events...
47310        
47311     },
47312
47313
47314     getResizeEl : function(){
47315         return this.wrap;
47316     },
47317
47318     getPositionEl : function(){
47319         return this.wrap;
47320     },
47321
47322     // private
47323     onRender : function(ct, position){
47324         
47325         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47326         //if(this.inputValue !== undefined){
47327         this.wrap = this.el.wrap();
47328         
47329         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47330         
47331         if (this.bodyStyle) {
47332             this.viewEl.applyStyles(this.bodyStyle);
47333         }
47334         //this.viewEl.setStyle('padding', '2px');
47335         
47336         this.setValue(this.value);
47337         
47338     },
47339 /*
47340     // private
47341     initValue : Roo.emptyFn,
47342
47343   */
47344
47345         // private
47346     onClick : function(){
47347         
47348     },
47349
47350     /**
47351      * Sets the checked state of the checkbox.
47352      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47353      */
47354     setValue : function(v){
47355         this.value = v;
47356         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47357         // this might be called before we have a dom element..
47358         if (!this.viewEl) {
47359             return;
47360         }
47361         this.viewEl.dom.innerHTML = html;
47362         Roo.form.DisplayField.superclass.setValue.call(this, v);
47363
47364     }
47365 });/*
47366  * 
47367  * Licence- LGPL
47368  * 
47369  */
47370
47371 /**
47372  * @class Roo.form.DayPicker
47373  * @extends Roo.form.Field
47374  * A Day picker show [M] [T] [W] ....
47375  * @constructor
47376  * Creates a new Day Picker
47377  * @param {Object} config Configuration options
47378  */
47379 Roo.form.DayPicker= function(config){
47380     Roo.form.DayPicker.superclass.constructor.call(this, config);
47381      
47382 };
47383
47384 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47385     /**
47386      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47387      */
47388     focusClass : undefined,
47389     /**
47390      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47391      */
47392     fieldClass: "x-form-field",
47393    
47394     /**
47395      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47396      * {tag: "input", type: "checkbox", autocomplete: "off"})
47397      */
47398     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47399     
47400    
47401     actionMode : 'viewEl', 
47402     //
47403     // private
47404  
47405     inputType : 'hidden',
47406     
47407      
47408     inputElement: false, // real input element?
47409     basedOn: false, // ????
47410     
47411     isFormField: true, // not sure where this is needed!!!!
47412
47413     onResize : function(){
47414         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47415         if(!this.boxLabel){
47416             this.el.alignTo(this.wrap, 'c-c');
47417         }
47418     },
47419
47420     initEvents : function(){
47421         Roo.form.Checkbox.superclass.initEvents.call(this);
47422         this.el.on("click", this.onClick,  this);
47423         this.el.on("change", this.onClick,  this);
47424     },
47425
47426
47427     getResizeEl : function(){
47428         return this.wrap;
47429     },
47430
47431     getPositionEl : function(){
47432         return this.wrap;
47433     },
47434
47435     
47436     // private
47437     onRender : function(ct, position){
47438         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47439        
47440         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47441         
47442         var r1 = '<table><tr>';
47443         var r2 = '<tr class="x-form-daypick-icons">';
47444         for (var i=0; i < 7; i++) {
47445             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47446             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47447         }
47448         
47449         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47450         viewEl.select('img').on('click', this.onClick, this);
47451         this.viewEl = viewEl;   
47452         
47453         
47454         // this will not work on Chrome!!!
47455         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47456         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47457         
47458         
47459           
47460
47461     },
47462
47463     // private
47464     initValue : Roo.emptyFn,
47465
47466     /**
47467      * Returns the checked state of the checkbox.
47468      * @return {Boolean} True if checked, else false
47469      */
47470     getValue : function(){
47471         return this.el.dom.value;
47472         
47473     },
47474
47475         // private
47476     onClick : function(e){ 
47477         //this.setChecked(!this.checked);
47478         Roo.get(e.target).toggleClass('x-menu-item-checked');
47479         this.refreshValue();
47480         //if(this.el.dom.checked != this.checked){
47481         //    this.setValue(this.el.dom.checked);
47482        // }
47483     },
47484     
47485     // private
47486     refreshValue : function()
47487     {
47488         var val = '';
47489         this.viewEl.select('img',true).each(function(e,i,n)  {
47490             val += e.is(".x-menu-item-checked") ? String(n) : '';
47491         });
47492         this.setValue(val, true);
47493     },
47494
47495     /**
47496      * Sets the checked state of the checkbox.
47497      * On is always based on a string comparison between inputValue and the param.
47498      * @param {Boolean/String} value - the value to set 
47499      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47500      */
47501     setValue : function(v,suppressEvent){
47502         if (!this.el.dom) {
47503             return;
47504         }
47505         var old = this.el.dom.value ;
47506         this.el.dom.value = v;
47507         if (suppressEvent) {
47508             return ;
47509         }
47510          
47511         // update display..
47512         this.viewEl.select('img',true).each(function(e,i,n)  {
47513             
47514             var on = e.is(".x-menu-item-checked");
47515             var newv = v.indexOf(String(n)) > -1;
47516             if (on != newv) {
47517                 e.toggleClass('x-menu-item-checked');
47518             }
47519             
47520         });
47521         
47522         
47523         this.fireEvent('change', this, v, old);
47524         
47525         
47526     },
47527    
47528     // handle setting of hidden value by some other method!!?!?
47529     setFromHidden: function()
47530     {
47531         if(!this.el){
47532             return;
47533         }
47534         //console.log("SET FROM HIDDEN");
47535         //alert('setFrom hidden');
47536         this.setValue(this.el.dom.value);
47537     },
47538     
47539     onDestroy : function()
47540     {
47541         if(this.viewEl){
47542             Roo.get(this.viewEl).remove();
47543         }
47544          
47545         Roo.form.DayPicker.superclass.onDestroy.call(this);
47546     }
47547
47548 });/*
47549  * RooJS Library 1.1.1
47550  * Copyright(c) 2008-2011  Alan Knowles
47551  *
47552  * License - LGPL
47553  */
47554  
47555
47556 /**
47557  * @class Roo.form.ComboCheck
47558  * @extends Roo.form.ComboBox
47559  * A combobox for multiple select items.
47560  *
47561  * FIXME - could do with a reset button..
47562  * 
47563  * @constructor
47564  * Create a new ComboCheck
47565  * @param {Object} config Configuration options
47566  */
47567 Roo.form.ComboCheck = function(config){
47568     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47569     // should verify some data...
47570     // like
47571     // hiddenName = required..
47572     // displayField = required
47573     // valudField == required
47574     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47575     var _t = this;
47576     Roo.each(req, function(e) {
47577         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47578             throw "Roo.form.ComboCheck : missing value for: " + e;
47579         }
47580     });
47581     
47582     
47583 };
47584
47585 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47586      
47587      
47588     editable : false,
47589      
47590     selectedClass: 'x-menu-item-checked', 
47591     
47592     // private
47593     onRender : function(ct, position){
47594         var _t = this;
47595         
47596         
47597         
47598         if(!this.tpl){
47599             var cls = 'x-combo-list';
47600
47601             
47602             this.tpl =  new Roo.Template({
47603                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47604                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47605                    '<span>{' + this.displayField + '}</span>' +
47606                     '</div>' 
47607                 
47608             });
47609         }
47610  
47611         
47612         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47613         this.view.singleSelect = false;
47614         this.view.multiSelect = true;
47615         this.view.toggleSelect = true;
47616         this.pageTb.add(new Roo.Toolbar.Fill(), {
47617             
47618             text: 'Done',
47619             handler: function()
47620             {
47621                 _t.collapse();
47622             }
47623         });
47624     },
47625     
47626     onViewOver : function(e, t){
47627         // do nothing...
47628         return;
47629         
47630     },
47631     
47632     onViewClick : function(doFocus,index){
47633         return;
47634         
47635     },
47636     select: function () {
47637         //Roo.log("SELECT CALLED");
47638     },
47639      
47640     selectByValue : function(xv, scrollIntoView){
47641         var ar = this.getValueArray();
47642         var sels = [];
47643         
47644         Roo.each(ar, function(v) {
47645             if(v === undefined || v === null){
47646                 return;
47647             }
47648             var r = this.findRecord(this.valueField, v);
47649             if(r){
47650                 sels.push(this.store.indexOf(r))
47651                 
47652             }
47653         },this);
47654         this.view.select(sels);
47655         return false;
47656     },
47657     
47658     
47659     
47660     onSelect : function(record, index){
47661        // Roo.log("onselect Called");
47662        // this is only called by the clear button now..
47663         this.view.clearSelections();
47664         this.setValue('[]');
47665         if (this.value != this.valueBefore) {
47666             this.fireEvent('change', this, this.value, this.valueBefore);
47667             this.valueBefore = this.value;
47668         }
47669     },
47670     getValueArray : function()
47671     {
47672         var ar = [] ;
47673         
47674         try {
47675             //Roo.log(this.value);
47676             if (typeof(this.value) == 'undefined') {
47677                 return [];
47678             }
47679             var ar = Roo.decode(this.value);
47680             return  ar instanceof Array ? ar : []; //?? valid?
47681             
47682         } catch(e) {
47683             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47684             return [];
47685         }
47686          
47687     },
47688     expand : function ()
47689     {
47690         
47691         Roo.form.ComboCheck.superclass.expand.call(this);
47692         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47693         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47694         
47695
47696     },
47697     
47698     collapse : function(){
47699         Roo.form.ComboCheck.superclass.collapse.call(this);
47700         var sl = this.view.getSelectedIndexes();
47701         var st = this.store;
47702         var nv = [];
47703         var tv = [];
47704         var r;
47705         Roo.each(sl, function(i) {
47706             r = st.getAt(i);
47707             nv.push(r.get(this.valueField));
47708         },this);
47709         this.setValue(Roo.encode(nv));
47710         if (this.value != this.valueBefore) {
47711
47712             this.fireEvent('change', this, this.value, this.valueBefore);
47713             this.valueBefore = this.value;
47714         }
47715         
47716     },
47717     
47718     setValue : function(v){
47719         // Roo.log(v);
47720         this.value = v;
47721         
47722         var vals = this.getValueArray();
47723         var tv = [];
47724         Roo.each(vals, function(k) {
47725             var r = this.findRecord(this.valueField, k);
47726             if(r){
47727                 tv.push(r.data[this.displayField]);
47728             }else if(this.valueNotFoundText !== undefined){
47729                 tv.push( this.valueNotFoundText );
47730             }
47731         },this);
47732        // Roo.log(tv);
47733         
47734         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47735         this.hiddenField.value = v;
47736         this.value = v;
47737     }
47738     
47739 });/*
47740  * Based on:
47741  * Ext JS Library 1.1.1
47742  * Copyright(c) 2006-2007, Ext JS, LLC.
47743  *
47744  * Originally Released Under LGPL - original licence link has changed is not relivant.
47745  *
47746  * Fork - LGPL
47747  * <script type="text/javascript">
47748  */
47749  
47750 /**
47751  * @class Roo.form.Signature
47752  * @extends Roo.form.Field
47753  * Signature field.  
47754  * @constructor
47755  * 
47756  * @param {Object} config Configuration options
47757  */
47758
47759 Roo.form.Signature = function(config){
47760     Roo.form.Signature.superclass.constructor.call(this, config);
47761     
47762     this.addEvents({// not in used??
47763          /**
47764          * @event confirm
47765          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47766              * @param {Roo.form.Signature} combo This combo box
47767              */
47768         'confirm' : true,
47769         /**
47770          * @event reset
47771          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47772              * @param {Roo.form.ComboBox} combo This combo box
47773              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47774              */
47775         'reset' : true
47776     });
47777 };
47778
47779 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47780     /**
47781      * @cfg {Object} labels Label to use when rendering a form.
47782      * defaults to 
47783      * labels : { 
47784      *      clear : "Clear",
47785      *      confirm : "Confirm"
47786      *  }
47787      */
47788     labels : { 
47789         clear : "Clear",
47790         confirm : "Confirm"
47791     },
47792     /**
47793      * @cfg {Number} width The signature panel width (defaults to 300)
47794      */
47795     width: 300,
47796     /**
47797      * @cfg {Number} height The signature panel height (defaults to 100)
47798      */
47799     height : 100,
47800     /**
47801      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47802      */
47803     allowBlank : false,
47804     
47805     //private
47806     // {Object} signPanel The signature SVG panel element (defaults to {})
47807     signPanel : {},
47808     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47809     isMouseDown : false,
47810     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47811     isConfirmed : false,
47812     // {String} signatureTmp SVG mapping string (defaults to empty string)
47813     signatureTmp : '',
47814     
47815     
47816     defaultAutoCreate : { // modified by initCompnoent..
47817         tag: "input",
47818         type:"hidden"
47819     },
47820
47821     // private
47822     onRender : function(ct, position){
47823         
47824         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47825         
47826         this.wrap = this.el.wrap({
47827             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47828         });
47829         
47830         this.createToolbar(this);
47831         this.signPanel = this.wrap.createChild({
47832                 tag: 'div',
47833                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47834             }, this.el
47835         );
47836             
47837         this.svgID = Roo.id();
47838         this.svgEl = this.signPanel.createChild({
47839               xmlns : 'http://www.w3.org/2000/svg',
47840               tag : 'svg',
47841               id : this.svgID + "-svg",
47842               width: this.width,
47843               height: this.height,
47844               viewBox: '0 0 '+this.width+' '+this.height,
47845               cn : [
47846                 {
47847                     tag: "rect",
47848                     id: this.svgID + "-svg-r",
47849                     width: this.width,
47850                     height: this.height,
47851                     fill: "#ffa"
47852                 },
47853                 {
47854                     tag: "line",
47855                     id: this.svgID + "-svg-l",
47856                     x1: "0", // start
47857                     y1: (this.height*0.8), // start set the line in 80% of height
47858                     x2: this.width, // end
47859                     y2: (this.height*0.8), // end set the line in 80% of height
47860                     'stroke': "#666",
47861                     'stroke-width': "1",
47862                     'stroke-dasharray': "3",
47863                     'shape-rendering': "crispEdges",
47864                     'pointer-events': "none"
47865                 },
47866                 {
47867                     tag: "path",
47868                     id: this.svgID + "-svg-p",
47869                     'stroke': "navy",
47870                     'stroke-width': "3",
47871                     'fill': "none",
47872                     'pointer-events': 'none'
47873                 }
47874               ]
47875         });
47876         this.createSVG();
47877         this.svgBox = this.svgEl.dom.getScreenCTM();
47878     },
47879     createSVG : function(){ 
47880         var svg = this.signPanel;
47881         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47882         var t = this;
47883
47884         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47885         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47886         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47887         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47888         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47889         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47890         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47891         
47892     },
47893     isTouchEvent : function(e){
47894         return e.type.match(/^touch/);
47895     },
47896     getCoords : function (e) {
47897         var pt    = this.svgEl.dom.createSVGPoint();
47898         pt.x = e.clientX; 
47899         pt.y = e.clientY;
47900         if (this.isTouchEvent(e)) {
47901             pt.x =  e.targetTouches[0].clientX;
47902             pt.y = e.targetTouches[0].clientY;
47903         }
47904         var a = this.svgEl.dom.getScreenCTM();
47905         var b = a.inverse();
47906         var mx = pt.matrixTransform(b);
47907         return mx.x + ',' + mx.y;
47908     },
47909     //mouse event headler 
47910     down : function (e) {
47911         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47912         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47913         
47914         this.isMouseDown = true;
47915         
47916         e.preventDefault();
47917     },
47918     move : function (e) {
47919         if (this.isMouseDown) {
47920             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47921             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47922         }
47923         
47924         e.preventDefault();
47925     },
47926     up : function (e) {
47927         this.isMouseDown = false;
47928         var sp = this.signatureTmp.split(' ');
47929         
47930         if(sp.length > 1){
47931             if(!sp[sp.length-2].match(/^L/)){
47932                 sp.pop();
47933                 sp.pop();
47934                 sp.push("");
47935                 this.signatureTmp = sp.join(" ");
47936             }
47937         }
47938         if(this.getValue() != this.signatureTmp){
47939             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47940             this.isConfirmed = false;
47941         }
47942         e.preventDefault();
47943     },
47944     
47945     /**
47946      * Protected method that will not generally be called directly. It
47947      * is called when the editor creates its toolbar. Override this method if you need to
47948      * add custom toolbar buttons.
47949      * @param {HtmlEditor} editor
47950      */
47951     createToolbar : function(editor){
47952          function btn(id, toggle, handler){
47953             var xid = fid + '-'+ id ;
47954             return {
47955                 id : xid,
47956                 cmd : id,
47957                 cls : 'x-btn-icon x-edit-'+id,
47958                 enableToggle:toggle !== false,
47959                 scope: editor, // was editor...
47960                 handler:handler||editor.relayBtnCmd,
47961                 clickEvent:'mousedown',
47962                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47963                 tabIndex:-1
47964             };
47965         }
47966         
47967         
47968         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47969         this.tb = tb;
47970         this.tb.add(
47971            {
47972                 cls : ' x-signature-btn x-signature-'+id,
47973                 scope: editor, // was editor...
47974                 handler: this.reset,
47975                 clickEvent:'mousedown',
47976                 text: this.labels.clear
47977             },
47978             {
47979                  xtype : 'Fill',
47980                  xns: Roo.Toolbar
47981             }, 
47982             {
47983                 cls : '  x-signature-btn x-signature-'+id,
47984                 scope: editor, // was editor...
47985                 handler: this.confirmHandler,
47986                 clickEvent:'mousedown',
47987                 text: this.labels.confirm
47988             }
47989         );
47990     
47991     },
47992     //public
47993     /**
47994      * when user is clicked confirm then show this image.....
47995      * 
47996      * @return {String} Image Data URI
47997      */
47998     getImageDataURI : function(){
47999         var svg = this.svgEl.dom.parentNode.innerHTML;
48000         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
48001         return src; 
48002     },
48003     /**
48004      * 
48005      * @return {Boolean} this.isConfirmed
48006      */
48007     getConfirmed : function(){
48008         return this.isConfirmed;
48009     },
48010     /**
48011      * 
48012      * @return {Number} this.width
48013      */
48014     getWidth : function(){
48015         return this.width;
48016     },
48017     /**
48018      * 
48019      * @return {Number} this.height
48020      */
48021     getHeight : function(){
48022         return this.height;
48023     },
48024     // private
48025     getSignature : function(){
48026         return this.signatureTmp;
48027     },
48028     // private
48029     reset : function(){
48030         this.signatureTmp = '';
48031         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48032         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48033         this.isConfirmed = false;
48034         Roo.form.Signature.superclass.reset.call(this);
48035     },
48036     setSignature : function(s){
48037         this.signatureTmp = s;
48038         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48039         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48040         this.setValue(s);
48041         this.isConfirmed = false;
48042         Roo.form.Signature.superclass.reset.call(this);
48043     }, 
48044     test : function(){
48045 //        Roo.log(this.signPanel.dom.contentWindow.up())
48046     },
48047     //private
48048     setConfirmed : function(){
48049         
48050         
48051         
48052 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48053     },
48054     // private
48055     confirmHandler : function(){
48056         if(!this.getSignature()){
48057             return;
48058         }
48059         
48060         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48061         this.setValue(this.getSignature());
48062         this.isConfirmed = true;
48063         
48064         this.fireEvent('confirm', this);
48065     },
48066     // private
48067     // Subclasses should provide the validation implementation by overriding this
48068     validateValue : function(value){
48069         if(this.allowBlank){
48070             return true;
48071         }
48072         
48073         if(this.isConfirmed){
48074             return true;
48075         }
48076         return false;
48077     }
48078 });/*
48079  * Based on:
48080  * Ext JS Library 1.1.1
48081  * Copyright(c) 2006-2007, Ext JS, LLC.
48082  *
48083  * Originally Released Under LGPL - original licence link has changed is not relivant.
48084  *
48085  * Fork - LGPL
48086  * <script type="text/javascript">
48087  */
48088  
48089
48090 /**
48091  * @class Roo.form.ComboBox
48092  * @extends Roo.form.TriggerField
48093  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48094  * @constructor
48095  * Create a new ComboBox.
48096  * @param {Object} config Configuration options
48097  */
48098 Roo.form.Select = function(config){
48099     Roo.form.Select.superclass.constructor.call(this, config);
48100      
48101 };
48102
48103 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48104     /**
48105      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48106      */
48107     /**
48108      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48109      * rendering into an Roo.Editor, defaults to false)
48110      */
48111     /**
48112      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48113      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48114      */
48115     /**
48116      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48117      */
48118     /**
48119      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48120      * the dropdown list (defaults to undefined, with no header element)
48121      */
48122
48123      /**
48124      * @cfg {String/Roo.Template} tpl The template to use to render the output
48125      */
48126      
48127     // private
48128     defaultAutoCreate : {tag: "select"  },
48129     /**
48130      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48131      */
48132     listWidth: undefined,
48133     /**
48134      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48135      * mode = 'remote' or 'text' if mode = 'local')
48136      */
48137     displayField: undefined,
48138     /**
48139      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48140      * mode = 'remote' or 'value' if mode = 'local'). 
48141      * Note: use of a valueField requires the user make a selection
48142      * in order for a value to be mapped.
48143      */
48144     valueField: undefined,
48145     
48146     
48147     /**
48148      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48149      * field's data value (defaults to the underlying DOM element's name)
48150      */
48151     hiddenName: undefined,
48152     /**
48153      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48154      */
48155     listClass: '',
48156     /**
48157      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48158      */
48159     selectedClass: 'x-combo-selected',
48160     /**
48161      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48162      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48163      * which displays a downward arrow icon).
48164      */
48165     triggerClass : 'x-form-arrow-trigger',
48166     /**
48167      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48168      */
48169     shadow:'sides',
48170     /**
48171      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48172      * anchor positions (defaults to 'tl-bl')
48173      */
48174     listAlign: 'tl-bl?',
48175     /**
48176      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48177      */
48178     maxHeight: 300,
48179     /**
48180      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48181      * query specified by the allQuery config option (defaults to 'query')
48182      */
48183     triggerAction: 'query',
48184     /**
48185      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48186      * (defaults to 4, does not apply if editable = false)
48187      */
48188     minChars : 4,
48189     /**
48190      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48191      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48192      */
48193     typeAhead: false,
48194     /**
48195      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48196      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48197      */
48198     queryDelay: 500,
48199     /**
48200      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48201      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48202      */
48203     pageSize: 0,
48204     /**
48205      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48206      * when editable = true (defaults to false)
48207      */
48208     selectOnFocus:false,
48209     /**
48210      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48211      */
48212     queryParam: 'query',
48213     /**
48214      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48215      * when mode = 'remote' (defaults to 'Loading...')
48216      */
48217     loadingText: 'Loading...',
48218     /**
48219      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48220      */
48221     resizable: false,
48222     /**
48223      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48224      */
48225     handleHeight : 8,
48226     /**
48227      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48228      * traditional select (defaults to true)
48229      */
48230     editable: true,
48231     /**
48232      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48233      */
48234     allQuery: '',
48235     /**
48236      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48237      */
48238     mode: 'remote',
48239     /**
48240      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48241      * listWidth has a higher value)
48242      */
48243     minListWidth : 70,
48244     /**
48245      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48246      * allow the user to set arbitrary text into the field (defaults to false)
48247      */
48248     forceSelection:false,
48249     /**
48250      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48251      * if typeAhead = true (defaults to 250)
48252      */
48253     typeAheadDelay : 250,
48254     /**
48255      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48256      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48257      */
48258     valueNotFoundText : undefined,
48259     
48260     /**
48261      * @cfg {String} defaultValue The value displayed after loading the store.
48262      */
48263     defaultValue: '',
48264     
48265     /**
48266      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48267      */
48268     blockFocus : false,
48269     
48270     /**
48271      * @cfg {Boolean} disableClear Disable showing of clear button.
48272      */
48273     disableClear : false,
48274     /**
48275      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48276      */
48277     alwaysQuery : false,
48278     
48279     //private
48280     addicon : false,
48281     editicon: false,
48282     
48283     // element that contains real text value.. (when hidden is used..)
48284      
48285     // private
48286     onRender : function(ct, position){
48287         Roo.form.Field.prototype.onRender.call(this, ct, position);
48288         
48289         if(this.store){
48290             this.store.on('beforeload', this.onBeforeLoad, this);
48291             this.store.on('load', this.onLoad, this);
48292             this.store.on('loadexception', this.onLoadException, this);
48293             this.store.load({});
48294         }
48295         
48296         
48297         
48298     },
48299
48300     // private
48301     initEvents : function(){
48302         //Roo.form.ComboBox.superclass.initEvents.call(this);
48303  
48304     },
48305
48306     onDestroy : function(){
48307        
48308         if(this.store){
48309             this.store.un('beforeload', this.onBeforeLoad, this);
48310             this.store.un('load', this.onLoad, this);
48311             this.store.un('loadexception', this.onLoadException, this);
48312         }
48313         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48314     },
48315
48316     // private
48317     fireKey : function(e){
48318         if(e.isNavKeyPress() && !this.list.isVisible()){
48319             this.fireEvent("specialkey", this, e);
48320         }
48321     },
48322
48323     // private
48324     onResize: function(w, h){
48325         
48326         return; 
48327     
48328         
48329     },
48330
48331     /**
48332      * Allow or prevent the user from directly editing the field text.  If false is passed,
48333      * the user will only be able to select from the items defined in the dropdown list.  This method
48334      * is the runtime equivalent of setting the 'editable' config option at config time.
48335      * @param {Boolean} value True to allow the user to directly edit the field text
48336      */
48337     setEditable : function(value){
48338          
48339     },
48340
48341     // private
48342     onBeforeLoad : function(){
48343         
48344         Roo.log("Select before load");
48345         return;
48346     
48347         this.innerList.update(this.loadingText ?
48348                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48349         //this.restrictHeight();
48350         this.selectedIndex = -1;
48351     },
48352
48353     // private
48354     onLoad : function(){
48355
48356     
48357         var dom = this.el.dom;
48358         dom.innerHTML = '';
48359          var od = dom.ownerDocument;
48360          
48361         if (this.emptyText) {
48362             var op = od.createElement('option');
48363             op.setAttribute('value', '');
48364             op.innerHTML = String.format('{0}', this.emptyText);
48365             dom.appendChild(op);
48366         }
48367         if(this.store.getCount() > 0){
48368            
48369             var vf = this.valueField;
48370             var df = this.displayField;
48371             this.store.data.each(function(r) {
48372                 // which colmsn to use... testing - cdoe / title..
48373                 var op = od.createElement('option');
48374                 op.setAttribute('value', r.data[vf]);
48375                 op.innerHTML = String.format('{0}', r.data[df]);
48376                 dom.appendChild(op);
48377             });
48378             if (typeof(this.defaultValue != 'undefined')) {
48379                 this.setValue(this.defaultValue);
48380             }
48381             
48382              
48383         }else{
48384             //this.onEmptyResults();
48385         }
48386         //this.el.focus();
48387     },
48388     // private
48389     onLoadException : function()
48390     {
48391         dom.innerHTML = '';
48392             
48393         Roo.log("Select on load exception");
48394         return;
48395     
48396         this.collapse();
48397         Roo.log(this.store.reader.jsonData);
48398         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48399             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48400         }
48401         
48402         
48403     },
48404     // private
48405     onTypeAhead : function(){
48406          
48407     },
48408
48409     // private
48410     onSelect : function(record, index){
48411         Roo.log('on select?');
48412         return;
48413         if(this.fireEvent('beforeselect', this, record, index) !== false){
48414             this.setFromData(index > -1 ? record.data : false);
48415             this.collapse();
48416             this.fireEvent('select', this, record, index);
48417         }
48418     },
48419
48420     /**
48421      * Returns the currently selected field value or empty string if no value is set.
48422      * @return {String} value The selected value
48423      */
48424     getValue : function(){
48425         var dom = this.el.dom;
48426         this.value = dom.options[dom.selectedIndex].value;
48427         return this.value;
48428         
48429     },
48430
48431     /**
48432      * Clears any text/value currently set in the field
48433      */
48434     clearValue : function(){
48435         this.value = '';
48436         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48437         
48438     },
48439
48440     /**
48441      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48442      * will be displayed in the field.  If the value does not match the data value of an existing item,
48443      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48444      * Otherwise the field will be blank (although the value will still be set).
48445      * @param {String} value The value to match
48446      */
48447     setValue : function(v){
48448         var d = this.el.dom;
48449         for (var i =0; i < d.options.length;i++) {
48450             if (v == d.options[i].value) {
48451                 d.selectedIndex = i;
48452                 this.value = v;
48453                 return;
48454             }
48455         }
48456         this.clearValue();
48457     },
48458     /**
48459      * @property {Object} the last set data for the element
48460      */
48461     
48462     lastData : false,
48463     /**
48464      * Sets the value of the field based on a object which is related to the record format for the store.
48465      * @param {Object} value the value to set as. or false on reset?
48466      */
48467     setFromData : function(o){
48468         Roo.log('setfrom data?');
48469          
48470         
48471         
48472     },
48473     // private
48474     reset : function(){
48475         this.clearValue();
48476     },
48477     // private
48478     findRecord : function(prop, value){
48479         
48480         return false;
48481     
48482         var record;
48483         if(this.store.getCount() > 0){
48484             this.store.each(function(r){
48485                 if(r.data[prop] == value){
48486                     record = r;
48487                     return false;
48488                 }
48489                 return true;
48490             });
48491         }
48492         return record;
48493     },
48494     
48495     getName: function()
48496     {
48497         // returns hidden if it's set..
48498         if (!this.rendered) {return ''};
48499         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48500         
48501     },
48502      
48503
48504     
48505
48506     // private
48507     onEmptyResults : function(){
48508         Roo.log('empty results');
48509         //this.collapse();
48510     },
48511
48512     /**
48513      * Returns true if the dropdown list is expanded, else false.
48514      */
48515     isExpanded : function(){
48516         return false;
48517     },
48518
48519     /**
48520      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48521      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48522      * @param {String} value The data value of the item to select
48523      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48524      * selected item if it is not currently in view (defaults to true)
48525      * @return {Boolean} True if the value matched an item in the list, else false
48526      */
48527     selectByValue : function(v, scrollIntoView){
48528         Roo.log('select By Value');
48529         return false;
48530     
48531         if(v !== undefined && v !== null){
48532             var r = this.findRecord(this.valueField || this.displayField, v);
48533             if(r){
48534                 this.select(this.store.indexOf(r), scrollIntoView);
48535                 return true;
48536             }
48537         }
48538         return false;
48539     },
48540
48541     /**
48542      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48543      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48544      * @param {Number} index The zero-based index of the list item to select
48545      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48546      * selected item if it is not currently in view (defaults to true)
48547      */
48548     select : function(index, scrollIntoView){
48549         Roo.log('select ');
48550         return  ;
48551         
48552         this.selectedIndex = index;
48553         this.view.select(index);
48554         if(scrollIntoView !== false){
48555             var el = this.view.getNode(index);
48556             if(el){
48557                 this.innerList.scrollChildIntoView(el, false);
48558             }
48559         }
48560     },
48561
48562       
48563
48564     // private
48565     validateBlur : function(){
48566         
48567         return;
48568         
48569     },
48570
48571     // private
48572     initQuery : function(){
48573         this.doQuery(this.getRawValue());
48574     },
48575
48576     // private
48577     doForce : function(){
48578         if(this.el.dom.value.length > 0){
48579             this.el.dom.value =
48580                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48581              
48582         }
48583     },
48584
48585     /**
48586      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48587      * query allowing the query action to be canceled if needed.
48588      * @param {String} query The SQL query to execute
48589      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48590      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48591      * saved in the current store (defaults to false)
48592      */
48593     doQuery : function(q, forceAll){
48594         
48595         Roo.log('doQuery?');
48596         if(q === undefined || q === null){
48597             q = '';
48598         }
48599         var qe = {
48600             query: q,
48601             forceAll: forceAll,
48602             combo: this,
48603             cancel:false
48604         };
48605         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48606             return false;
48607         }
48608         q = qe.query;
48609         forceAll = qe.forceAll;
48610         if(forceAll === true || (q.length >= this.minChars)){
48611             if(this.lastQuery != q || this.alwaysQuery){
48612                 this.lastQuery = q;
48613                 if(this.mode == 'local'){
48614                     this.selectedIndex = -1;
48615                     if(forceAll){
48616                         this.store.clearFilter();
48617                     }else{
48618                         this.store.filter(this.displayField, q);
48619                     }
48620                     this.onLoad();
48621                 }else{
48622                     this.store.baseParams[this.queryParam] = q;
48623                     this.store.load({
48624                         params: this.getParams(q)
48625                     });
48626                     this.expand();
48627                 }
48628             }else{
48629                 this.selectedIndex = -1;
48630                 this.onLoad();   
48631             }
48632         }
48633     },
48634
48635     // private
48636     getParams : function(q){
48637         var p = {};
48638         //p[this.queryParam] = q;
48639         if(this.pageSize){
48640             p.start = 0;
48641             p.limit = this.pageSize;
48642         }
48643         return p;
48644     },
48645
48646     /**
48647      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48648      */
48649     collapse : function(){
48650         
48651     },
48652
48653     // private
48654     collapseIf : function(e){
48655         
48656     },
48657
48658     /**
48659      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48660      */
48661     expand : function(){
48662         
48663     } ,
48664
48665     // private
48666      
48667
48668     /** 
48669     * @cfg {Boolean} grow 
48670     * @hide 
48671     */
48672     /** 
48673     * @cfg {Number} growMin 
48674     * @hide 
48675     */
48676     /** 
48677     * @cfg {Number} growMax 
48678     * @hide 
48679     */
48680     /**
48681      * @hide
48682      * @method autoSize
48683      */
48684     
48685     setWidth : function()
48686     {
48687         
48688     },
48689     getResizeEl : function(){
48690         return this.el;
48691     }
48692 });//<script type="text/javasscript">
48693  
48694
48695 /**
48696  * @class Roo.DDView
48697  * A DnD enabled version of Roo.View.
48698  * @param {Element/String} container The Element in which to create the View.
48699  * @param {String} tpl The template string used to create the markup for each element of the View
48700  * @param {Object} config The configuration properties. These include all the config options of
48701  * {@link Roo.View} plus some specific to this class.<br>
48702  * <p>
48703  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48704  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48705  * <p>
48706  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48707 .x-view-drag-insert-above {
48708         border-top:1px dotted #3366cc;
48709 }
48710 .x-view-drag-insert-below {
48711         border-bottom:1px dotted #3366cc;
48712 }
48713 </code></pre>
48714  * 
48715  */
48716  
48717 Roo.DDView = function(container, tpl, config) {
48718     Roo.DDView.superclass.constructor.apply(this, arguments);
48719     this.getEl().setStyle("outline", "0px none");
48720     this.getEl().unselectable();
48721     if (this.dragGroup) {
48722                 this.setDraggable(this.dragGroup.split(","));
48723     }
48724     if (this.dropGroup) {
48725                 this.setDroppable(this.dropGroup.split(","));
48726     }
48727     if (this.deletable) {
48728         this.setDeletable();
48729     }
48730     this.isDirtyFlag = false;
48731         this.addEvents({
48732                 "drop" : true
48733         });
48734 };
48735
48736 Roo.extend(Roo.DDView, Roo.View, {
48737 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48738 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48739 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48740 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48741
48742         isFormField: true,
48743
48744         reset: Roo.emptyFn,
48745         
48746         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48747
48748         validate: function() {
48749                 return true;
48750         },
48751         
48752         destroy: function() {
48753                 this.purgeListeners();
48754                 this.getEl.removeAllListeners();
48755                 this.getEl().remove();
48756                 if (this.dragZone) {
48757                         if (this.dragZone.destroy) {
48758                                 this.dragZone.destroy();
48759                         }
48760                 }
48761                 if (this.dropZone) {
48762                         if (this.dropZone.destroy) {
48763                                 this.dropZone.destroy();
48764                         }
48765                 }
48766         },
48767
48768 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48769         getName: function() {
48770                 return this.name;
48771         },
48772
48773 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48774         setValue: function(v) {
48775                 if (!this.store) {
48776                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48777                 }
48778                 var data = {};
48779                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48780                 this.store.proxy = new Roo.data.MemoryProxy(data);
48781                 this.store.load();
48782         },
48783
48784 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48785         getValue: function() {
48786                 var result = '(';
48787                 this.store.each(function(rec) {
48788                         result += rec.id + ',';
48789                 });
48790                 return result.substr(0, result.length - 1) + ')';
48791         },
48792         
48793         getIds: function() {
48794                 var i = 0, result = new Array(this.store.getCount());
48795                 this.store.each(function(rec) {
48796                         result[i++] = rec.id;
48797                 });
48798                 return result;
48799         },
48800         
48801         isDirty: function() {
48802                 return this.isDirtyFlag;
48803         },
48804
48805 /**
48806  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48807  *      whole Element becomes the target, and this causes the drop gesture to append.
48808  */
48809     getTargetFromEvent : function(e) {
48810                 var target = e.getTarget();
48811                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48812                 target = target.parentNode;
48813                 }
48814                 if (!target) {
48815                         target = this.el.dom.lastChild || this.el.dom;
48816                 }
48817                 return target;
48818     },
48819
48820 /**
48821  *      Create the drag data which consists of an object which has the property "ddel" as
48822  *      the drag proxy element. 
48823  */
48824     getDragData : function(e) {
48825         var target = this.findItemFromChild(e.getTarget());
48826                 if(target) {
48827                         this.handleSelection(e);
48828                         var selNodes = this.getSelectedNodes();
48829             var dragData = {
48830                 source: this,
48831                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48832                 nodes: selNodes,
48833                 records: []
48834                         };
48835                         var selectedIndices = this.getSelectedIndexes();
48836                         for (var i = 0; i < selectedIndices.length; i++) {
48837                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48838                         }
48839                         if (selNodes.length == 1) {
48840                                 dragData.ddel = target.cloneNode(true); // the div element
48841                         } else {
48842                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48843                                 div.className = 'multi-proxy';
48844                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48845                                         div.appendChild(selNodes[i].cloneNode(true));
48846                                 }
48847                                 dragData.ddel = div;
48848                         }
48849             //console.log(dragData)
48850             //console.log(dragData.ddel.innerHTML)
48851                         return dragData;
48852                 }
48853         //console.log('nodragData')
48854                 return false;
48855     },
48856     
48857 /**     Specify to which ddGroup items in this DDView may be dragged. */
48858     setDraggable: function(ddGroup) {
48859         if (ddGroup instanceof Array) {
48860                 Roo.each(ddGroup, this.setDraggable, this);
48861                 return;
48862         }
48863         if (this.dragZone) {
48864                 this.dragZone.addToGroup(ddGroup);
48865         } else {
48866                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48867                                 containerScroll: true,
48868                                 ddGroup: ddGroup 
48869
48870                         });
48871 //                      Draggability implies selection. DragZone's mousedown selects the element.
48872                         if (!this.multiSelect) { this.singleSelect = true; }
48873
48874 //                      Wire the DragZone's handlers up to methods in *this*
48875                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48876                 }
48877     },
48878
48879 /**     Specify from which ddGroup this DDView accepts drops. */
48880     setDroppable: function(ddGroup) {
48881         if (ddGroup instanceof Array) {
48882                 Roo.each(ddGroup, this.setDroppable, this);
48883                 return;
48884         }
48885         if (this.dropZone) {
48886                 this.dropZone.addToGroup(ddGroup);
48887         } else {
48888                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48889                                 containerScroll: true,
48890                                 ddGroup: ddGroup
48891                         });
48892
48893 //                      Wire the DropZone's handlers up to methods in *this*
48894                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48895                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48896                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48897                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48898                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48899                 }
48900     },
48901
48902 /**     Decide whether to drop above or below a View node. */
48903     getDropPoint : function(e, n, dd){
48904         if (n == this.el.dom) { return "above"; }
48905                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48906                 var c = t + (b - t) / 2;
48907                 var y = Roo.lib.Event.getPageY(e);
48908                 if(y <= c) {
48909                         return "above";
48910                 }else{
48911                         return "below";
48912                 }
48913     },
48914
48915     onNodeEnter : function(n, dd, e, data){
48916                 return false;
48917     },
48918     
48919     onNodeOver : function(n, dd, e, data){
48920                 var pt = this.getDropPoint(e, n, dd);
48921                 // set the insert point style on the target node
48922                 var dragElClass = this.dropNotAllowed;
48923                 if (pt) {
48924                         var targetElClass;
48925                         if (pt == "above"){
48926                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48927                                 targetElClass = "x-view-drag-insert-above";
48928                         } else {
48929                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48930                                 targetElClass = "x-view-drag-insert-below";
48931                         }
48932                         if (this.lastInsertClass != targetElClass){
48933                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48934                                 this.lastInsertClass = targetElClass;
48935                         }
48936                 }
48937                 return dragElClass;
48938         },
48939
48940     onNodeOut : function(n, dd, e, data){
48941                 this.removeDropIndicators(n);
48942     },
48943
48944     onNodeDrop : function(n, dd, e, data){
48945         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48946                 return false;
48947         }
48948         var pt = this.getDropPoint(e, n, dd);
48949                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48950                 if (pt == "below") { insertAt++; }
48951                 for (var i = 0; i < data.records.length; i++) {
48952                         var r = data.records[i];
48953                         var dup = this.store.getById(r.id);
48954                         if (dup && (dd != this.dragZone)) {
48955                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48956                         } else {
48957                                 if (data.copy) {
48958                                         this.store.insert(insertAt++, r.copy());
48959                                 } else {
48960                                         data.source.isDirtyFlag = true;
48961                                         r.store.remove(r);
48962                                         this.store.insert(insertAt++, r);
48963                                 }
48964                                 this.isDirtyFlag = true;
48965                         }
48966                 }
48967                 this.dragZone.cachedTarget = null;
48968                 return true;
48969     },
48970
48971     removeDropIndicators : function(n){
48972                 if(n){
48973                         Roo.fly(n).removeClass([
48974                                 "x-view-drag-insert-above",
48975                                 "x-view-drag-insert-below"]);
48976                         this.lastInsertClass = "_noclass";
48977                 }
48978     },
48979
48980 /**
48981  *      Utility method. Add a delete option to the DDView's context menu.
48982  *      @param {String} imageUrl The URL of the "delete" icon image.
48983  */
48984         setDeletable: function(imageUrl) {
48985                 if (!this.singleSelect && !this.multiSelect) {
48986                         this.singleSelect = true;
48987                 }
48988                 var c = this.getContextMenu();
48989                 this.contextMenu.on("itemclick", function(item) {
48990                         switch (item.id) {
48991                                 case "delete":
48992                                         this.remove(this.getSelectedIndexes());
48993                                         break;
48994                         }
48995                 }, this);
48996                 this.contextMenu.add({
48997                         icon: imageUrl,
48998                         id: "delete",
48999                         text: 'Delete'
49000                 });
49001         },
49002         
49003 /**     Return the context menu for this DDView. */
49004         getContextMenu: function() {
49005                 if (!this.contextMenu) {
49006 //                      Create the View's context menu
49007                         this.contextMenu = new Roo.menu.Menu({
49008                                 id: this.id + "-contextmenu"
49009                         });
49010                         this.el.on("contextmenu", this.showContextMenu, this);
49011                 }
49012                 return this.contextMenu;
49013         },
49014         
49015         disableContextMenu: function() {
49016                 if (this.contextMenu) {
49017                         this.el.un("contextmenu", this.showContextMenu, this);
49018                 }
49019         },
49020
49021         showContextMenu: function(e, item) {
49022         item = this.findItemFromChild(e.getTarget());
49023                 if (item) {
49024                         e.stopEvent();
49025                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49026                         this.contextMenu.showAt(e.getXY());
49027             }
49028     },
49029
49030 /**
49031  *      Remove {@link Roo.data.Record}s at the specified indices.
49032  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49033  */
49034     remove: function(selectedIndices) {
49035                 selectedIndices = [].concat(selectedIndices);
49036                 for (var i = 0; i < selectedIndices.length; i++) {
49037                         var rec = this.store.getAt(selectedIndices[i]);
49038                         this.store.remove(rec);
49039                 }
49040     },
49041
49042 /**
49043  *      Double click fires the event, but also, if this is draggable, and there is only one other
49044  *      related DropZone, it transfers the selected node.
49045  */
49046     onDblClick : function(e){
49047         var item = this.findItemFromChild(e.getTarget());
49048         if(item){
49049             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49050                 return false;
49051             }
49052             if (this.dragGroup) {
49053                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49054                     while (targets.indexOf(this.dropZone) > -1) {
49055                             targets.remove(this.dropZone);
49056                                 }
49057                     if (targets.length == 1) {
49058                                         this.dragZone.cachedTarget = null;
49059                         var el = Roo.get(targets[0].getEl());
49060                         var box = el.getBox(true);
49061                         targets[0].onNodeDrop(el.dom, {
49062                                 target: el.dom,
49063                                 xy: [box.x, box.y + box.height - 1]
49064                         }, null, this.getDragData(e));
49065                     }
49066                 }
49067         }
49068     },
49069     
49070     handleSelection: function(e) {
49071                 this.dragZone.cachedTarget = null;
49072         var item = this.findItemFromChild(e.getTarget());
49073         if (!item) {
49074                 this.clearSelections(true);
49075                 return;
49076         }
49077                 if (item && (this.multiSelect || this.singleSelect)){
49078                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49079                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49080                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49081                                 this.unselect(item);
49082                         } else {
49083                                 this.select(item, this.multiSelect && e.ctrlKey);
49084                                 this.lastSelection = item;
49085                         }
49086                 }
49087     },
49088
49089     onItemClick : function(item, index, e){
49090                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49091                         return false;
49092                 }
49093                 return true;
49094     },
49095
49096     unselect : function(nodeInfo, suppressEvent){
49097                 var node = this.getNode(nodeInfo);
49098                 if(node && this.isSelected(node)){
49099                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49100                                 Roo.fly(node).removeClass(this.selectedClass);
49101                                 this.selections.remove(node);
49102                                 if(!suppressEvent){
49103                                         this.fireEvent("selectionchange", this, this.selections);
49104                                 }
49105                         }
49106                 }
49107     }
49108 });
49109 /*
49110  * Based on:
49111  * Ext JS Library 1.1.1
49112  * Copyright(c) 2006-2007, Ext JS, LLC.
49113  *
49114  * Originally Released Under LGPL - original licence link has changed is not relivant.
49115  *
49116  * Fork - LGPL
49117  * <script type="text/javascript">
49118  */
49119  
49120 /**
49121  * @class Roo.LayoutManager
49122  * @extends Roo.util.Observable
49123  * Base class for layout managers.
49124  */
49125 Roo.LayoutManager = function(container, config){
49126     Roo.LayoutManager.superclass.constructor.call(this);
49127     this.el = Roo.get(container);
49128     // ie scrollbar fix
49129     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49130         document.body.scroll = "no";
49131     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49132         this.el.position('relative');
49133     }
49134     this.id = this.el.id;
49135     this.el.addClass("x-layout-container");
49136     /** false to disable window resize monitoring @type Boolean */
49137     this.monitorWindowResize = true;
49138     this.regions = {};
49139     this.addEvents({
49140         /**
49141          * @event layout
49142          * Fires when a layout is performed. 
49143          * @param {Roo.LayoutManager} this
49144          */
49145         "layout" : true,
49146         /**
49147          * @event regionresized
49148          * Fires when the user resizes a region. 
49149          * @param {Roo.LayoutRegion} region The resized region
49150          * @param {Number} newSize The new size (width for east/west, height for north/south)
49151          */
49152         "regionresized" : true,
49153         /**
49154          * @event regioncollapsed
49155          * Fires when a region is collapsed. 
49156          * @param {Roo.LayoutRegion} region The collapsed region
49157          */
49158         "regioncollapsed" : true,
49159         /**
49160          * @event regionexpanded
49161          * Fires when a region is expanded.  
49162          * @param {Roo.LayoutRegion} region The expanded region
49163          */
49164         "regionexpanded" : true
49165     });
49166     this.updating = false;
49167     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49168 };
49169
49170 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49171     /**
49172      * Returns true if this layout is currently being updated
49173      * @return {Boolean}
49174      */
49175     isUpdating : function(){
49176         return this.updating; 
49177     },
49178     
49179     /**
49180      * Suspend the LayoutManager from doing auto-layouts while
49181      * making multiple add or remove calls
49182      */
49183     beginUpdate : function(){
49184         this.updating = true;    
49185     },
49186     
49187     /**
49188      * Restore auto-layouts and optionally disable the manager from performing a layout
49189      * @param {Boolean} noLayout true to disable a layout update 
49190      */
49191     endUpdate : function(noLayout){
49192         this.updating = false;
49193         if(!noLayout){
49194             this.layout();
49195         }    
49196     },
49197     
49198     layout: function(){
49199         
49200     },
49201     
49202     onRegionResized : function(region, newSize){
49203         this.fireEvent("regionresized", region, newSize);
49204         this.layout();
49205     },
49206     
49207     onRegionCollapsed : function(region){
49208         this.fireEvent("regioncollapsed", region);
49209     },
49210     
49211     onRegionExpanded : function(region){
49212         this.fireEvent("regionexpanded", region);
49213     },
49214         
49215     /**
49216      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49217      * performs box-model adjustments.
49218      * @return {Object} The size as an object {width: (the width), height: (the height)}
49219      */
49220     getViewSize : function(){
49221         var size;
49222         if(this.el.dom != document.body){
49223             size = this.el.getSize();
49224         }else{
49225             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49226         }
49227         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49228         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49229         return size;
49230     },
49231     
49232     /**
49233      * Returns the Element this layout is bound to.
49234      * @return {Roo.Element}
49235      */
49236     getEl : function(){
49237         return this.el;
49238     },
49239     
49240     /**
49241      * Returns the specified region.
49242      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49243      * @return {Roo.LayoutRegion}
49244      */
49245     getRegion : function(target){
49246         return this.regions[target.toLowerCase()];
49247     },
49248     
49249     onWindowResize : function(){
49250         if(this.monitorWindowResize){
49251             this.layout();
49252         }
49253     }
49254 });/*
49255  * Based on:
49256  * Ext JS Library 1.1.1
49257  * Copyright(c) 2006-2007, Ext JS, LLC.
49258  *
49259  * Originally Released Under LGPL - original licence link has changed is not relivant.
49260  *
49261  * Fork - LGPL
49262  * <script type="text/javascript">
49263  */
49264 /**
49265  * @class Roo.BorderLayout
49266  * @extends Roo.LayoutManager
49267  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49268  * please see: <br><br>
49269  * <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>
49270  * <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>
49271  * Example:
49272  <pre><code>
49273  var layout = new Roo.BorderLayout(document.body, {
49274     north: {
49275         initialSize: 25,
49276         titlebar: false
49277     },
49278     west: {
49279         split:true,
49280         initialSize: 200,
49281         minSize: 175,
49282         maxSize: 400,
49283         titlebar: true,
49284         collapsible: true
49285     },
49286     east: {
49287         split:true,
49288         initialSize: 202,
49289         minSize: 175,
49290         maxSize: 400,
49291         titlebar: true,
49292         collapsible: true
49293     },
49294     south: {
49295         split:true,
49296         initialSize: 100,
49297         minSize: 100,
49298         maxSize: 200,
49299         titlebar: true,
49300         collapsible: true
49301     },
49302     center: {
49303         titlebar: true,
49304         autoScroll:true,
49305         resizeTabs: true,
49306         minTabWidth: 50,
49307         preferredTabWidth: 150
49308     }
49309 });
49310
49311 // shorthand
49312 var CP = Roo.ContentPanel;
49313
49314 layout.beginUpdate();
49315 layout.add("north", new CP("north", "North"));
49316 layout.add("south", new CP("south", {title: "South", closable: true}));
49317 layout.add("west", new CP("west", {title: "West"}));
49318 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49319 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49320 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49321 layout.getRegion("center").showPanel("center1");
49322 layout.endUpdate();
49323 </code></pre>
49324
49325 <b>The container the layout is rendered into can be either the body element or any other element.
49326 If it is not the body element, the container needs to either be an absolute positioned element,
49327 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49328 the container size if it is not the body element.</b>
49329
49330 * @constructor
49331 * Create a new BorderLayout
49332 * @param {String/HTMLElement/Element} container The container this layout is bound to
49333 * @param {Object} config Configuration options
49334  */
49335 Roo.BorderLayout = function(container, config){
49336     config = config || {};
49337     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49338     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49339     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49340         var target = this.factory.validRegions[i];
49341         if(config[target]){
49342             this.addRegion(target, config[target]);
49343         }
49344     }
49345 };
49346
49347 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49348     /**
49349      * Creates and adds a new region if it doesn't already exist.
49350      * @param {String} target The target region key (north, south, east, west or center).
49351      * @param {Object} config The regions config object
49352      * @return {BorderLayoutRegion} The new region
49353      */
49354     addRegion : function(target, config){
49355         if(!this.regions[target]){
49356             var r = this.factory.create(target, this, config);
49357             this.bindRegion(target, r);
49358         }
49359         return this.regions[target];
49360     },
49361
49362     // private (kinda)
49363     bindRegion : function(name, r){
49364         this.regions[name] = r;
49365         r.on("visibilitychange", this.layout, this);
49366         r.on("paneladded", this.layout, this);
49367         r.on("panelremoved", this.layout, this);
49368         r.on("invalidated", this.layout, this);
49369         r.on("resized", this.onRegionResized, this);
49370         r.on("collapsed", this.onRegionCollapsed, this);
49371         r.on("expanded", this.onRegionExpanded, this);
49372     },
49373
49374     /**
49375      * Performs a layout update.
49376      */
49377     layout : function(){
49378         if(this.updating) return;
49379         var size = this.getViewSize();
49380         var w = size.width;
49381         var h = size.height;
49382         var centerW = w;
49383         var centerH = h;
49384         var centerY = 0;
49385         var centerX = 0;
49386         //var x = 0, y = 0;
49387
49388         var rs = this.regions;
49389         var north = rs["north"];
49390         var south = rs["south"]; 
49391         var west = rs["west"];
49392         var east = rs["east"];
49393         var center = rs["center"];
49394         //if(this.hideOnLayout){ // not supported anymore
49395             //c.el.setStyle("display", "none");
49396         //}
49397         if(north && north.isVisible()){
49398             var b = north.getBox();
49399             var m = north.getMargins();
49400             b.width = w - (m.left+m.right);
49401             b.x = m.left;
49402             b.y = m.top;
49403             centerY = b.height + b.y + m.bottom;
49404             centerH -= centerY;
49405             north.updateBox(this.safeBox(b));
49406         }
49407         if(south && south.isVisible()){
49408             var b = south.getBox();
49409             var m = south.getMargins();
49410             b.width = w - (m.left+m.right);
49411             b.x = m.left;
49412             var totalHeight = (b.height + m.top + m.bottom);
49413             b.y = h - totalHeight + m.top;
49414             centerH -= totalHeight;
49415             south.updateBox(this.safeBox(b));
49416         }
49417         if(west && west.isVisible()){
49418             var b = west.getBox();
49419             var m = west.getMargins();
49420             b.height = centerH - (m.top+m.bottom);
49421             b.x = m.left;
49422             b.y = centerY + m.top;
49423             var totalWidth = (b.width + m.left + m.right);
49424             centerX += totalWidth;
49425             centerW -= totalWidth;
49426             west.updateBox(this.safeBox(b));
49427         }
49428         if(east && east.isVisible()){
49429             var b = east.getBox();
49430             var m = east.getMargins();
49431             b.height = centerH - (m.top+m.bottom);
49432             var totalWidth = (b.width + m.left + m.right);
49433             b.x = w - totalWidth + m.left;
49434             b.y = centerY + m.top;
49435             centerW -= totalWidth;
49436             east.updateBox(this.safeBox(b));
49437         }
49438         if(center){
49439             var m = center.getMargins();
49440             var centerBox = {
49441                 x: centerX + m.left,
49442                 y: centerY + m.top,
49443                 width: centerW - (m.left+m.right),
49444                 height: centerH - (m.top+m.bottom)
49445             };
49446             //if(this.hideOnLayout){
49447                 //center.el.setStyle("display", "block");
49448             //}
49449             center.updateBox(this.safeBox(centerBox));
49450         }
49451         this.el.repaint();
49452         this.fireEvent("layout", this);
49453     },
49454
49455     // private
49456     safeBox : function(box){
49457         box.width = Math.max(0, box.width);
49458         box.height = Math.max(0, box.height);
49459         return box;
49460     },
49461
49462     /**
49463      * Adds a ContentPanel (or subclass) to this layout.
49464      * @param {String} target The target region key (north, south, east, west or center).
49465      * @param {Roo.ContentPanel} panel The panel to add
49466      * @return {Roo.ContentPanel} The added panel
49467      */
49468     add : function(target, panel){
49469          
49470         target = target.toLowerCase();
49471         return this.regions[target].add(panel);
49472     },
49473
49474     /**
49475      * Remove a ContentPanel (or subclass) to this layout.
49476      * @param {String} target The target region key (north, south, east, west or center).
49477      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49478      * @return {Roo.ContentPanel} The removed panel
49479      */
49480     remove : function(target, panel){
49481         target = target.toLowerCase();
49482         return this.regions[target].remove(panel);
49483     },
49484
49485     /**
49486      * Searches all regions for a panel with the specified id
49487      * @param {String} panelId
49488      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49489      */
49490     findPanel : function(panelId){
49491         var rs = this.regions;
49492         for(var target in rs){
49493             if(typeof rs[target] != "function"){
49494                 var p = rs[target].getPanel(panelId);
49495                 if(p){
49496                     return p;
49497                 }
49498             }
49499         }
49500         return null;
49501     },
49502
49503     /**
49504      * Searches all regions for a panel with the specified id and activates (shows) it.
49505      * @param {String/ContentPanel} panelId The panels id or the panel itself
49506      * @return {Roo.ContentPanel} The shown panel or null
49507      */
49508     showPanel : function(panelId) {
49509       var rs = this.regions;
49510       for(var target in rs){
49511          var r = rs[target];
49512          if(typeof r != "function"){
49513             if(r.hasPanel(panelId)){
49514                return r.showPanel(panelId);
49515             }
49516          }
49517       }
49518       return null;
49519    },
49520
49521    /**
49522      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49523      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49524      */
49525     restoreState : function(provider){
49526         if(!provider){
49527             provider = Roo.state.Manager;
49528         }
49529         var sm = new Roo.LayoutStateManager();
49530         sm.init(this, provider);
49531     },
49532
49533     /**
49534      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49535      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49536      * a valid ContentPanel config object.  Example:
49537      * <pre><code>
49538 // Create the main layout
49539 var layout = new Roo.BorderLayout('main-ct', {
49540     west: {
49541         split:true,
49542         minSize: 175,
49543         titlebar: true
49544     },
49545     center: {
49546         title:'Components'
49547     }
49548 }, 'main-ct');
49549
49550 // Create and add multiple ContentPanels at once via configs
49551 layout.batchAdd({
49552    west: {
49553        id: 'source-files',
49554        autoCreate:true,
49555        title:'Ext Source Files',
49556        autoScroll:true,
49557        fitToFrame:true
49558    },
49559    center : {
49560        el: cview,
49561        autoScroll:true,
49562        fitToFrame:true,
49563        toolbar: tb,
49564        resizeEl:'cbody'
49565    }
49566 });
49567 </code></pre>
49568      * @param {Object} regions An object containing ContentPanel configs by region name
49569      */
49570     batchAdd : function(regions){
49571         this.beginUpdate();
49572         for(var rname in regions){
49573             var lr = this.regions[rname];
49574             if(lr){
49575                 this.addTypedPanels(lr, regions[rname]);
49576             }
49577         }
49578         this.endUpdate();
49579     },
49580
49581     // private
49582     addTypedPanels : function(lr, ps){
49583         if(typeof ps == 'string'){
49584             lr.add(new Roo.ContentPanel(ps));
49585         }
49586         else if(ps instanceof Array){
49587             for(var i =0, len = ps.length; i < len; i++){
49588                 this.addTypedPanels(lr, ps[i]);
49589             }
49590         }
49591         else if(!ps.events){ // raw config?
49592             var el = ps.el;
49593             delete ps.el; // prevent conflict
49594             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49595         }
49596         else {  // panel object assumed!
49597             lr.add(ps);
49598         }
49599     },
49600     /**
49601      * Adds a xtype elements to the layout.
49602      * <pre><code>
49603
49604 layout.addxtype({
49605        xtype : 'ContentPanel',
49606        region: 'west',
49607        items: [ .... ]
49608    }
49609 );
49610
49611 layout.addxtype({
49612         xtype : 'NestedLayoutPanel',
49613         region: 'west',
49614         layout: {
49615            center: { },
49616            west: { }   
49617         },
49618         items : [ ... list of content panels or nested layout panels.. ]
49619    }
49620 );
49621 </code></pre>
49622      * @param {Object} cfg Xtype definition of item to add.
49623      */
49624     addxtype : function(cfg)
49625     {
49626         // basically accepts a pannel...
49627         // can accept a layout region..!?!?
49628         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49629         
49630         if (!cfg.xtype.match(/Panel$/)) {
49631             return false;
49632         }
49633         var ret = false;
49634         
49635         if (typeof(cfg.region) == 'undefined') {
49636             Roo.log("Failed to add Panel, region was not set");
49637             Roo.log(cfg);
49638             return false;
49639         }
49640         var region = cfg.region;
49641         delete cfg.region;
49642         
49643           
49644         var xitems = [];
49645         if (cfg.items) {
49646             xitems = cfg.items;
49647             delete cfg.items;
49648         }
49649         var nb = false;
49650         
49651         switch(cfg.xtype) 
49652         {
49653             case 'ContentPanel':  // ContentPanel (el, cfg)
49654             case 'ScrollPanel':  // ContentPanel (el, cfg)
49655             case 'ViewPanel': 
49656                 if(cfg.autoCreate) {
49657                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49658                 } else {
49659                     var el = this.el.createChild();
49660                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49661                 }
49662                 
49663                 this.add(region, ret);
49664                 break;
49665             
49666             
49667             case 'TreePanel': // our new panel!
49668                 cfg.el = this.el.createChild();
49669                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49670                 this.add(region, ret);
49671                 break;
49672             
49673             case 'NestedLayoutPanel': 
49674                 // create a new Layout (which is  a Border Layout...
49675                 var el = this.el.createChild();
49676                 var clayout = cfg.layout;
49677                 delete cfg.layout;
49678                 clayout.items   = clayout.items  || [];
49679                 // replace this exitems with the clayout ones..
49680                 xitems = clayout.items;
49681                  
49682                 
49683                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49684                     cfg.background = false;
49685                 }
49686                 var layout = new Roo.BorderLayout(el, clayout);
49687                 
49688                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49689                 //console.log('adding nested layout panel '  + cfg.toSource());
49690                 this.add(region, ret);
49691                 nb = {}; /// find first...
49692                 break;
49693                 
49694             case 'GridPanel': 
49695             
49696                 // needs grid and region
49697                 
49698                 //var el = this.getRegion(region).el.createChild();
49699                 var el = this.el.createChild();
49700                 // create the grid first...
49701                 
49702                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49703                 delete cfg.grid;
49704                 if (region == 'center' && this.active ) {
49705                     cfg.background = false;
49706                 }
49707                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49708                 
49709                 this.add(region, ret);
49710                 if (cfg.background) {
49711                     ret.on('activate', function(gp) {
49712                         if (!gp.grid.rendered) {
49713                             gp.grid.render();
49714                         }
49715                     });
49716                 } else {
49717                     grid.render();
49718                 }
49719                 break;
49720            
49721            
49722            
49723                 
49724                 
49725                 
49726             default:
49727                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49728                     
49729                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49730                     this.add(region, ret);
49731                 } else {
49732                 
49733                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49734                     return null;
49735                 }
49736                 
49737              // GridPanel (grid, cfg)
49738             
49739         }
49740         this.beginUpdate();
49741         // add children..
49742         var region = '';
49743         var abn = {};
49744         Roo.each(xitems, function(i)  {
49745             region = nb && i.region ? i.region : false;
49746             
49747             var add = ret.addxtype(i);
49748            
49749             if (region) {
49750                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49751                 if (!i.background) {
49752                     abn[region] = nb[region] ;
49753                 }
49754             }
49755             
49756         });
49757         this.endUpdate();
49758
49759         // make the last non-background panel active..
49760         //if (nb) { Roo.log(abn); }
49761         if (nb) {
49762             
49763             for(var r in abn) {
49764                 region = this.getRegion(r);
49765                 if (region) {
49766                     // tried using nb[r], but it does not work..
49767                      
49768                     region.showPanel(abn[r]);
49769                    
49770                 }
49771             }
49772         }
49773         return ret;
49774         
49775     }
49776 });
49777
49778 /**
49779  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49780  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49781  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49782  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49783  * <pre><code>
49784 // shorthand
49785 var CP = Roo.ContentPanel;
49786
49787 var layout = Roo.BorderLayout.create({
49788     north: {
49789         initialSize: 25,
49790         titlebar: false,
49791         panels: [new CP("north", "North")]
49792     },
49793     west: {
49794         split:true,
49795         initialSize: 200,
49796         minSize: 175,
49797         maxSize: 400,
49798         titlebar: true,
49799         collapsible: true,
49800         panels: [new CP("west", {title: "West"})]
49801     },
49802     east: {
49803         split:true,
49804         initialSize: 202,
49805         minSize: 175,
49806         maxSize: 400,
49807         titlebar: true,
49808         collapsible: true,
49809         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49810     },
49811     south: {
49812         split:true,
49813         initialSize: 100,
49814         minSize: 100,
49815         maxSize: 200,
49816         titlebar: true,
49817         collapsible: true,
49818         panels: [new CP("south", {title: "South", closable: true})]
49819     },
49820     center: {
49821         titlebar: true,
49822         autoScroll:true,
49823         resizeTabs: true,
49824         minTabWidth: 50,
49825         preferredTabWidth: 150,
49826         panels: [
49827             new CP("center1", {title: "Close Me", closable: true}),
49828             new CP("center2", {title: "Center Panel", closable: false})
49829         ]
49830     }
49831 }, document.body);
49832
49833 layout.getRegion("center").showPanel("center1");
49834 </code></pre>
49835  * @param config
49836  * @param targetEl
49837  */
49838 Roo.BorderLayout.create = function(config, targetEl){
49839     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49840     layout.beginUpdate();
49841     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49842     for(var j = 0, jlen = regions.length; j < jlen; j++){
49843         var lr = regions[j];
49844         if(layout.regions[lr] && config[lr].panels){
49845             var r = layout.regions[lr];
49846             var ps = config[lr].panels;
49847             layout.addTypedPanels(r, ps);
49848         }
49849     }
49850     layout.endUpdate();
49851     return layout;
49852 };
49853
49854 // private
49855 Roo.BorderLayout.RegionFactory = {
49856     // private
49857     validRegions : ["north","south","east","west","center"],
49858
49859     // private
49860     create : function(target, mgr, config){
49861         target = target.toLowerCase();
49862         if(config.lightweight || config.basic){
49863             return new Roo.BasicLayoutRegion(mgr, config, target);
49864         }
49865         switch(target){
49866             case "north":
49867                 return new Roo.NorthLayoutRegion(mgr, config);
49868             case "south":
49869                 return new Roo.SouthLayoutRegion(mgr, config);
49870             case "east":
49871                 return new Roo.EastLayoutRegion(mgr, config);
49872             case "west":
49873                 return new Roo.WestLayoutRegion(mgr, config);
49874             case "center":
49875                 return new Roo.CenterLayoutRegion(mgr, config);
49876         }
49877         throw 'Layout region "'+target+'" not supported.';
49878     }
49879 };/*
49880  * Based on:
49881  * Ext JS Library 1.1.1
49882  * Copyright(c) 2006-2007, Ext JS, LLC.
49883  *
49884  * Originally Released Under LGPL - original licence link has changed is not relivant.
49885  *
49886  * Fork - LGPL
49887  * <script type="text/javascript">
49888  */
49889  
49890 /**
49891  * @class Roo.BasicLayoutRegion
49892  * @extends Roo.util.Observable
49893  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49894  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49895  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49896  */
49897 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49898     this.mgr = mgr;
49899     this.position  = pos;
49900     this.events = {
49901         /**
49902          * @scope Roo.BasicLayoutRegion
49903          */
49904         
49905         /**
49906          * @event beforeremove
49907          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49908          * @param {Roo.LayoutRegion} this
49909          * @param {Roo.ContentPanel} panel The panel
49910          * @param {Object} e The cancel event object
49911          */
49912         "beforeremove" : true,
49913         /**
49914          * @event invalidated
49915          * Fires when the layout for this region is changed.
49916          * @param {Roo.LayoutRegion} this
49917          */
49918         "invalidated" : true,
49919         /**
49920          * @event visibilitychange
49921          * Fires when this region is shown or hidden 
49922          * @param {Roo.LayoutRegion} this
49923          * @param {Boolean} visibility true or false
49924          */
49925         "visibilitychange" : true,
49926         /**
49927          * @event paneladded
49928          * Fires when a panel is added. 
49929          * @param {Roo.LayoutRegion} this
49930          * @param {Roo.ContentPanel} panel The panel
49931          */
49932         "paneladded" : true,
49933         /**
49934          * @event panelremoved
49935          * Fires when a panel is removed. 
49936          * @param {Roo.LayoutRegion} this
49937          * @param {Roo.ContentPanel} panel The panel
49938          */
49939         "panelremoved" : true,
49940         /**
49941          * @event collapsed
49942          * Fires when this region is collapsed.
49943          * @param {Roo.LayoutRegion} this
49944          */
49945         "collapsed" : true,
49946         /**
49947          * @event expanded
49948          * Fires when this region is expanded.
49949          * @param {Roo.LayoutRegion} this
49950          */
49951         "expanded" : true,
49952         /**
49953          * @event slideshow
49954          * Fires when this region is slid into view.
49955          * @param {Roo.LayoutRegion} this
49956          */
49957         "slideshow" : true,
49958         /**
49959          * @event slidehide
49960          * Fires when this region slides out of view. 
49961          * @param {Roo.LayoutRegion} this
49962          */
49963         "slidehide" : true,
49964         /**
49965          * @event panelactivated
49966          * Fires when a panel is activated. 
49967          * @param {Roo.LayoutRegion} this
49968          * @param {Roo.ContentPanel} panel The activated panel
49969          */
49970         "panelactivated" : true,
49971         /**
49972          * @event resized
49973          * Fires when the user resizes this region. 
49974          * @param {Roo.LayoutRegion} this
49975          * @param {Number} newSize The new size (width for east/west, height for north/south)
49976          */
49977         "resized" : true
49978     };
49979     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49980     this.panels = new Roo.util.MixedCollection();
49981     this.panels.getKey = this.getPanelId.createDelegate(this);
49982     this.box = null;
49983     this.activePanel = null;
49984     // ensure listeners are added...
49985     
49986     if (config.listeners || config.events) {
49987         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49988             listeners : config.listeners || {},
49989             events : config.events || {}
49990         });
49991     }
49992     
49993     if(skipConfig !== true){
49994         this.applyConfig(config);
49995     }
49996 };
49997
49998 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49999     getPanelId : function(p){
50000         return p.getId();
50001     },
50002     
50003     applyConfig : function(config){
50004         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50005         this.config = config;
50006         
50007     },
50008     
50009     /**
50010      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
50011      * the width, for horizontal (north, south) the height.
50012      * @param {Number} newSize The new width or height
50013      */
50014     resizeTo : function(newSize){
50015         var el = this.el ? this.el :
50016                  (this.activePanel ? this.activePanel.getEl() : null);
50017         if(el){
50018             switch(this.position){
50019                 case "east":
50020                 case "west":
50021                     el.setWidth(newSize);
50022                     this.fireEvent("resized", this, newSize);
50023                 break;
50024                 case "north":
50025                 case "south":
50026                     el.setHeight(newSize);
50027                     this.fireEvent("resized", this, newSize);
50028                 break;                
50029             }
50030         }
50031     },
50032     
50033     getBox : function(){
50034         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50035     },
50036     
50037     getMargins : function(){
50038         return this.margins;
50039     },
50040     
50041     updateBox : function(box){
50042         this.box = box;
50043         var el = this.activePanel.getEl();
50044         el.dom.style.left = box.x + "px";
50045         el.dom.style.top = box.y + "px";
50046         this.activePanel.setSize(box.width, box.height);
50047     },
50048     
50049     /**
50050      * Returns the container element for this region.
50051      * @return {Roo.Element}
50052      */
50053     getEl : function(){
50054         return this.activePanel;
50055     },
50056     
50057     /**
50058      * Returns true if this region is currently visible.
50059      * @return {Boolean}
50060      */
50061     isVisible : function(){
50062         return this.activePanel ? true : false;
50063     },
50064     
50065     setActivePanel : function(panel){
50066         panel = this.getPanel(panel);
50067         if(this.activePanel && this.activePanel != panel){
50068             this.activePanel.setActiveState(false);
50069             this.activePanel.getEl().setLeftTop(-10000,-10000);
50070         }
50071         this.activePanel = panel;
50072         panel.setActiveState(true);
50073         if(this.box){
50074             panel.setSize(this.box.width, this.box.height);
50075         }
50076         this.fireEvent("panelactivated", this, panel);
50077         this.fireEvent("invalidated");
50078     },
50079     
50080     /**
50081      * Show the specified panel.
50082      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50083      * @return {Roo.ContentPanel} The shown panel or null
50084      */
50085     showPanel : function(panel){
50086         if(panel = this.getPanel(panel)){
50087             this.setActivePanel(panel);
50088         }
50089         return panel;
50090     },
50091     
50092     /**
50093      * Get the active panel for this region.
50094      * @return {Roo.ContentPanel} The active panel or null
50095      */
50096     getActivePanel : function(){
50097         return this.activePanel;
50098     },
50099     
50100     /**
50101      * Add the passed ContentPanel(s)
50102      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50103      * @return {Roo.ContentPanel} The panel added (if only one was added)
50104      */
50105     add : function(panel){
50106         if(arguments.length > 1){
50107             for(var i = 0, len = arguments.length; i < len; i++) {
50108                 this.add(arguments[i]);
50109             }
50110             return null;
50111         }
50112         if(this.hasPanel(panel)){
50113             this.showPanel(panel);
50114             return panel;
50115         }
50116         var el = panel.getEl();
50117         if(el.dom.parentNode != this.mgr.el.dom){
50118             this.mgr.el.dom.appendChild(el.dom);
50119         }
50120         if(panel.setRegion){
50121             panel.setRegion(this);
50122         }
50123         this.panels.add(panel);
50124         el.setStyle("position", "absolute");
50125         if(!panel.background){
50126             this.setActivePanel(panel);
50127             if(this.config.initialSize && this.panels.getCount()==1){
50128                 this.resizeTo(this.config.initialSize);
50129             }
50130         }
50131         this.fireEvent("paneladded", this, panel);
50132         return panel;
50133     },
50134     
50135     /**
50136      * Returns true if the panel is in this region.
50137      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50138      * @return {Boolean}
50139      */
50140     hasPanel : function(panel){
50141         if(typeof panel == "object"){ // must be panel obj
50142             panel = panel.getId();
50143         }
50144         return this.getPanel(panel) ? true : false;
50145     },
50146     
50147     /**
50148      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50149      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50150      * @param {Boolean} preservePanel Overrides the config preservePanel option
50151      * @return {Roo.ContentPanel} The panel that was removed
50152      */
50153     remove : function(panel, preservePanel){
50154         panel = this.getPanel(panel);
50155         if(!panel){
50156             return null;
50157         }
50158         var e = {};
50159         this.fireEvent("beforeremove", this, panel, e);
50160         if(e.cancel === true){
50161             return null;
50162         }
50163         var panelId = panel.getId();
50164         this.panels.removeKey(panelId);
50165         return panel;
50166     },
50167     
50168     /**
50169      * Returns the panel specified or null if it's not in this region.
50170      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50171      * @return {Roo.ContentPanel}
50172      */
50173     getPanel : function(id){
50174         if(typeof id == "object"){ // must be panel obj
50175             return id;
50176         }
50177         return this.panels.get(id);
50178     },
50179     
50180     /**
50181      * Returns this regions position (north/south/east/west/center).
50182      * @return {String} 
50183      */
50184     getPosition: function(){
50185         return this.position;    
50186     }
50187 });/*
50188  * Based on:
50189  * Ext JS Library 1.1.1
50190  * Copyright(c) 2006-2007, Ext JS, LLC.
50191  *
50192  * Originally Released Under LGPL - original licence link has changed is not relivant.
50193  *
50194  * Fork - LGPL
50195  * <script type="text/javascript">
50196  */
50197  
50198 /**
50199  * @class Roo.LayoutRegion
50200  * @extends Roo.BasicLayoutRegion
50201  * This class represents a region in a layout manager.
50202  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50203  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50204  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50205  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50206  * @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})
50207  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50208  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50209  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50210  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50211  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50212  * @cfg {String}    title           The title for the region (overrides panel titles)
50213  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50214  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50215  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50216  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50217  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50218  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50219  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50220  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50221  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50222  * @cfg {Boolean}   showPin         True to show a pin button
50223  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50224  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50225  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50226  * @cfg {Number}    width           For East/West panels
50227  * @cfg {Number}    height          For North/South panels
50228  * @cfg {Boolean}   split           To show the splitter
50229  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50230  */
50231 Roo.LayoutRegion = function(mgr, config, pos){
50232     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50233     var dh = Roo.DomHelper;
50234     /** This region's container element 
50235     * @type Roo.Element */
50236     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50237     /** This region's title element 
50238     * @type Roo.Element */
50239
50240     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50241         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50242         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50243     ]}, true);
50244     this.titleEl.enableDisplayMode();
50245     /** This region's title text element 
50246     * @type HTMLElement */
50247     this.titleTextEl = this.titleEl.dom.firstChild;
50248     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50249     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50250     this.closeBtn.enableDisplayMode();
50251     this.closeBtn.on("click", this.closeClicked, this);
50252     this.closeBtn.hide();
50253
50254     this.createBody(config);
50255     this.visible = true;
50256     this.collapsed = false;
50257
50258     if(config.hideWhenEmpty){
50259         this.hide();
50260         this.on("paneladded", this.validateVisibility, this);
50261         this.on("panelremoved", this.validateVisibility, this);
50262     }
50263     this.applyConfig(config);
50264 };
50265
50266 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50267
50268     createBody : function(){
50269         /** This region's body element 
50270         * @type Roo.Element */
50271         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50272     },
50273
50274     applyConfig : function(c){
50275         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50276             var dh = Roo.DomHelper;
50277             if(c.titlebar !== false){
50278                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50279                 this.collapseBtn.on("click", this.collapse, this);
50280                 this.collapseBtn.enableDisplayMode();
50281
50282                 if(c.showPin === true || this.showPin){
50283                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50284                     this.stickBtn.enableDisplayMode();
50285                     this.stickBtn.on("click", this.expand, this);
50286                     this.stickBtn.hide();
50287                 }
50288             }
50289             /** This region's collapsed element
50290             * @type Roo.Element */
50291             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50292                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50293             ]}, true);
50294             if(c.floatable !== false){
50295                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50296                this.collapsedEl.on("click", this.collapseClick, this);
50297             }
50298
50299             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50300                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50301                    id: "message", unselectable: "on", style:{"float":"left"}});
50302                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50303              }
50304             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50305             this.expandBtn.on("click", this.expand, this);
50306         }
50307         if(this.collapseBtn){
50308             this.collapseBtn.setVisible(c.collapsible == true);
50309         }
50310         this.cmargins = c.cmargins || this.cmargins ||
50311                          (this.position == "west" || this.position == "east" ?
50312                              {top: 0, left: 2, right:2, bottom: 0} :
50313                              {top: 2, left: 0, right:0, bottom: 2});
50314         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50315         this.bottomTabs = c.tabPosition != "top";
50316         this.autoScroll = c.autoScroll || false;
50317         if(this.autoScroll){
50318             this.bodyEl.setStyle("overflow", "auto");
50319         }else{
50320             this.bodyEl.setStyle("overflow", "hidden");
50321         }
50322         //if(c.titlebar !== false){
50323             if((!c.titlebar && !c.title) || c.titlebar === false){
50324                 this.titleEl.hide();
50325             }else{
50326                 this.titleEl.show();
50327                 if(c.title){
50328                     this.titleTextEl.innerHTML = c.title;
50329                 }
50330             }
50331         //}
50332         this.duration = c.duration || .30;
50333         this.slideDuration = c.slideDuration || .45;
50334         this.config = c;
50335         if(c.collapsed){
50336             this.collapse(true);
50337         }
50338         if(c.hidden){
50339             this.hide();
50340         }
50341     },
50342     /**
50343      * Returns true if this region is currently visible.
50344      * @return {Boolean}
50345      */
50346     isVisible : function(){
50347         return this.visible;
50348     },
50349
50350     /**
50351      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50352      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50353      */
50354     setCollapsedTitle : function(title){
50355         title = title || "&#160;";
50356         if(this.collapsedTitleTextEl){
50357             this.collapsedTitleTextEl.innerHTML = title;
50358         }
50359     },
50360
50361     getBox : function(){
50362         var b;
50363         if(!this.collapsed){
50364             b = this.el.getBox(false, true);
50365         }else{
50366             b = this.collapsedEl.getBox(false, true);
50367         }
50368         return b;
50369     },
50370
50371     getMargins : function(){
50372         return this.collapsed ? this.cmargins : this.margins;
50373     },
50374
50375     highlight : function(){
50376         this.el.addClass("x-layout-panel-dragover");
50377     },
50378
50379     unhighlight : function(){
50380         this.el.removeClass("x-layout-panel-dragover");
50381     },
50382
50383     updateBox : function(box){
50384         this.box = box;
50385         if(!this.collapsed){
50386             this.el.dom.style.left = box.x + "px";
50387             this.el.dom.style.top = box.y + "px";
50388             this.updateBody(box.width, box.height);
50389         }else{
50390             this.collapsedEl.dom.style.left = box.x + "px";
50391             this.collapsedEl.dom.style.top = box.y + "px";
50392             this.collapsedEl.setSize(box.width, box.height);
50393         }
50394         if(this.tabs){
50395             this.tabs.autoSizeTabs();
50396         }
50397     },
50398
50399     updateBody : function(w, h){
50400         if(w !== null){
50401             this.el.setWidth(w);
50402             w -= this.el.getBorderWidth("rl");
50403             if(this.config.adjustments){
50404                 w += this.config.adjustments[0];
50405             }
50406         }
50407         if(h !== null){
50408             this.el.setHeight(h);
50409             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50410             h -= this.el.getBorderWidth("tb");
50411             if(this.config.adjustments){
50412                 h += this.config.adjustments[1];
50413             }
50414             this.bodyEl.setHeight(h);
50415             if(this.tabs){
50416                 h = this.tabs.syncHeight(h);
50417             }
50418         }
50419         if(this.panelSize){
50420             w = w !== null ? w : this.panelSize.width;
50421             h = h !== null ? h : this.panelSize.height;
50422         }
50423         if(this.activePanel){
50424             var el = this.activePanel.getEl();
50425             w = w !== null ? w : el.getWidth();
50426             h = h !== null ? h : el.getHeight();
50427             this.panelSize = {width: w, height: h};
50428             this.activePanel.setSize(w, h);
50429         }
50430         if(Roo.isIE && this.tabs){
50431             this.tabs.el.repaint();
50432         }
50433     },
50434
50435     /**
50436      * Returns the container element for this region.
50437      * @return {Roo.Element}
50438      */
50439     getEl : function(){
50440         return this.el;
50441     },
50442
50443     /**
50444      * Hides this region.
50445      */
50446     hide : function(){
50447         if(!this.collapsed){
50448             this.el.dom.style.left = "-2000px";
50449             this.el.hide();
50450         }else{
50451             this.collapsedEl.dom.style.left = "-2000px";
50452             this.collapsedEl.hide();
50453         }
50454         this.visible = false;
50455         this.fireEvent("visibilitychange", this, false);
50456     },
50457
50458     /**
50459      * Shows this region if it was previously hidden.
50460      */
50461     show : function(){
50462         if(!this.collapsed){
50463             this.el.show();
50464         }else{
50465             this.collapsedEl.show();
50466         }
50467         this.visible = true;
50468         this.fireEvent("visibilitychange", this, true);
50469     },
50470
50471     closeClicked : function(){
50472         if(this.activePanel){
50473             this.remove(this.activePanel);
50474         }
50475     },
50476
50477     collapseClick : function(e){
50478         if(this.isSlid){
50479            e.stopPropagation();
50480            this.slideIn();
50481         }else{
50482            e.stopPropagation();
50483            this.slideOut();
50484         }
50485     },
50486
50487     /**
50488      * Collapses this region.
50489      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50490      */
50491     collapse : function(skipAnim){
50492         if(this.collapsed) return;
50493         this.collapsed = true;
50494         if(this.split){
50495             this.split.el.hide();
50496         }
50497         if(this.config.animate && skipAnim !== true){
50498             this.fireEvent("invalidated", this);
50499             this.animateCollapse();
50500         }else{
50501             this.el.setLocation(-20000,-20000);
50502             this.el.hide();
50503             this.collapsedEl.show();
50504             this.fireEvent("collapsed", this);
50505             this.fireEvent("invalidated", this);
50506         }
50507     },
50508
50509     animateCollapse : function(){
50510         // overridden
50511     },
50512
50513     /**
50514      * Expands this region if it was previously collapsed.
50515      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50516      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50517      */
50518     expand : function(e, skipAnim){
50519         if(e) e.stopPropagation();
50520         if(!this.collapsed || this.el.hasActiveFx()) return;
50521         if(this.isSlid){
50522             this.afterSlideIn();
50523             skipAnim = true;
50524         }
50525         this.collapsed = false;
50526         if(this.config.animate && skipAnim !== true){
50527             this.animateExpand();
50528         }else{
50529             this.el.show();
50530             if(this.split){
50531                 this.split.el.show();
50532             }
50533             this.collapsedEl.setLocation(-2000,-2000);
50534             this.collapsedEl.hide();
50535             this.fireEvent("invalidated", this);
50536             this.fireEvent("expanded", this);
50537         }
50538     },
50539
50540     animateExpand : function(){
50541         // overridden
50542     },
50543
50544     initTabs : function()
50545     {
50546         this.bodyEl.setStyle("overflow", "hidden");
50547         var ts = new Roo.TabPanel(
50548                 this.bodyEl.dom,
50549                 {
50550                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50551                     disableTooltips: this.config.disableTabTips,
50552                     toolbar : this.config.toolbar
50553                 }
50554         );
50555         if(this.config.hideTabs){
50556             ts.stripWrap.setDisplayed(false);
50557         }
50558         this.tabs = ts;
50559         ts.resizeTabs = this.config.resizeTabs === true;
50560         ts.minTabWidth = this.config.minTabWidth || 40;
50561         ts.maxTabWidth = this.config.maxTabWidth || 250;
50562         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50563         ts.monitorResize = false;
50564         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50565         ts.bodyEl.addClass('x-layout-tabs-body');
50566         this.panels.each(this.initPanelAsTab, this);
50567     },
50568
50569     initPanelAsTab : function(panel){
50570         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50571                     this.config.closeOnTab && panel.isClosable());
50572         if(panel.tabTip !== undefined){
50573             ti.setTooltip(panel.tabTip);
50574         }
50575         ti.on("activate", function(){
50576               this.setActivePanel(panel);
50577         }, this);
50578         if(this.config.closeOnTab){
50579             ti.on("beforeclose", function(t, e){
50580                 e.cancel = true;
50581                 this.remove(panel);
50582             }, this);
50583         }
50584         return ti;
50585     },
50586
50587     updatePanelTitle : function(panel, title){
50588         if(this.activePanel == panel){
50589             this.updateTitle(title);
50590         }
50591         if(this.tabs){
50592             var ti = this.tabs.getTab(panel.getEl().id);
50593             ti.setText(title);
50594             if(panel.tabTip !== undefined){
50595                 ti.setTooltip(panel.tabTip);
50596             }
50597         }
50598     },
50599
50600     updateTitle : function(title){
50601         if(this.titleTextEl && !this.config.title){
50602             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50603         }
50604     },
50605
50606     setActivePanel : function(panel){
50607         panel = this.getPanel(panel);
50608         if(this.activePanel && this.activePanel != panel){
50609             this.activePanel.setActiveState(false);
50610         }
50611         this.activePanel = panel;
50612         panel.setActiveState(true);
50613         if(this.panelSize){
50614             panel.setSize(this.panelSize.width, this.panelSize.height);
50615         }
50616         if(this.closeBtn){
50617             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50618         }
50619         this.updateTitle(panel.getTitle());
50620         if(this.tabs){
50621             this.fireEvent("invalidated", this);
50622         }
50623         this.fireEvent("panelactivated", this, panel);
50624     },
50625
50626     /**
50627      * Shows the specified panel.
50628      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50629      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50630      */
50631     showPanel : function(panel)
50632     {
50633         panel = this.getPanel(panel);
50634         if(panel){
50635             if(this.tabs){
50636                 var tab = this.tabs.getTab(panel.getEl().id);
50637                 if(tab.isHidden()){
50638                     this.tabs.unhideTab(tab.id);
50639                 }
50640                 tab.activate();
50641             }else{
50642                 this.setActivePanel(panel);
50643             }
50644         }
50645         return panel;
50646     },
50647
50648     /**
50649      * Get the active panel for this region.
50650      * @return {Roo.ContentPanel} The active panel or null
50651      */
50652     getActivePanel : function(){
50653         return this.activePanel;
50654     },
50655
50656     validateVisibility : function(){
50657         if(this.panels.getCount() < 1){
50658             this.updateTitle("&#160;");
50659             this.closeBtn.hide();
50660             this.hide();
50661         }else{
50662             if(!this.isVisible()){
50663                 this.show();
50664             }
50665         }
50666     },
50667
50668     /**
50669      * Adds the passed ContentPanel(s) to this region.
50670      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50671      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50672      */
50673     add : function(panel){
50674         if(arguments.length > 1){
50675             for(var i = 0, len = arguments.length; i < len; i++) {
50676                 this.add(arguments[i]);
50677             }
50678             return null;
50679         }
50680         if(this.hasPanel(panel)){
50681             this.showPanel(panel);
50682             return panel;
50683         }
50684         panel.setRegion(this);
50685         this.panels.add(panel);
50686         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50687             this.bodyEl.dom.appendChild(panel.getEl().dom);
50688             if(panel.background !== true){
50689                 this.setActivePanel(panel);
50690             }
50691             this.fireEvent("paneladded", this, panel);
50692             return panel;
50693         }
50694         if(!this.tabs){
50695             this.initTabs();
50696         }else{
50697             this.initPanelAsTab(panel);
50698         }
50699         if(panel.background !== true){
50700             this.tabs.activate(panel.getEl().id);
50701         }
50702         this.fireEvent("paneladded", this, panel);
50703         return panel;
50704     },
50705
50706     /**
50707      * Hides the tab for the specified panel.
50708      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50709      */
50710     hidePanel : function(panel){
50711         if(this.tabs && (panel = this.getPanel(panel))){
50712             this.tabs.hideTab(panel.getEl().id);
50713         }
50714     },
50715
50716     /**
50717      * Unhides the tab for a previously hidden panel.
50718      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50719      */
50720     unhidePanel : function(panel){
50721         if(this.tabs && (panel = this.getPanel(panel))){
50722             this.tabs.unhideTab(panel.getEl().id);
50723         }
50724     },
50725
50726     clearPanels : function(){
50727         while(this.panels.getCount() > 0){
50728              this.remove(this.panels.first());
50729         }
50730     },
50731
50732     /**
50733      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50734      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50735      * @param {Boolean} preservePanel Overrides the config preservePanel option
50736      * @return {Roo.ContentPanel} The panel that was removed
50737      */
50738     remove : function(panel, preservePanel){
50739         panel = this.getPanel(panel);
50740         if(!panel){
50741             return null;
50742         }
50743         var e = {};
50744         this.fireEvent("beforeremove", this, panel, e);
50745         if(e.cancel === true){
50746             return null;
50747         }
50748         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50749         var panelId = panel.getId();
50750         this.panels.removeKey(panelId);
50751         if(preservePanel){
50752             document.body.appendChild(panel.getEl().dom);
50753         }
50754         if(this.tabs){
50755             this.tabs.removeTab(panel.getEl().id);
50756         }else if (!preservePanel){
50757             this.bodyEl.dom.removeChild(panel.getEl().dom);
50758         }
50759         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50760             var p = this.panels.first();
50761             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50762             tempEl.appendChild(p.getEl().dom);
50763             this.bodyEl.update("");
50764             this.bodyEl.dom.appendChild(p.getEl().dom);
50765             tempEl = null;
50766             this.updateTitle(p.getTitle());
50767             this.tabs = null;
50768             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50769             this.setActivePanel(p);
50770         }
50771         panel.setRegion(null);
50772         if(this.activePanel == panel){
50773             this.activePanel = null;
50774         }
50775         if(this.config.autoDestroy !== false && preservePanel !== true){
50776             try{panel.destroy();}catch(e){}
50777         }
50778         this.fireEvent("panelremoved", this, panel);
50779         return panel;
50780     },
50781
50782     /**
50783      * Returns the TabPanel component used by this region
50784      * @return {Roo.TabPanel}
50785      */
50786     getTabs : function(){
50787         return this.tabs;
50788     },
50789
50790     createTool : function(parentEl, className){
50791         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50792             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50793         btn.addClassOnOver("x-layout-tools-button-over");
50794         return btn;
50795     }
50796 });/*
50797  * Based on:
50798  * Ext JS Library 1.1.1
50799  * Copyright(c) 2006-2007, Ext JS, LLC.
50800  *
50801  * Originally Released Under LGPL - original licence link has changed is not relivant.
50802  *
50803  * Fork - LGPL
50804  * <script type="text/javascript">
50805  */
50806  
50807
50808
50809 /**
50810  * @class Roo.SplitLayoutRegion
50811  * @extends Roo.LayoutRegion
50812  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50813  */
50814 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50815     this.cursor = cursor;
50816     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50817 };
50818
50819 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50820     splitTip : "Drag to resize.",
50821     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50822     useSplitTips : false,
50823
50824     applyConfig : function(config){
50825         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50826         if(config.split){
50827             if(!this.split){
50828                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50829                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50830                 /** The SplitBar for this region 
50831                 * @type Roo.SplitBar */
50832                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50833                 this.split.on("moved", this.onSplitMove, this);
50834                 this.split.useShim = config.useShim === true;
50835                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50836                 if(this.useSplitTips){
50837                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50838                 }
50839                 if(config.collapsible){
50840                     this.split.el.on("dblclick", this.collapse,  this);
50841                 }
50842             }
50843             if(typeof config.minSize != "undefined"){
50844                 this.split.minSize = config.minSize;
50845             }
50846             if(typeof config.maxSize != "undefined"){
50847                 this.split.maxSize = config.maxSize;
50848             }
50849             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50850                 this.hideSplitter();
50851             }
50852         }
50853     },
50854
50855     getHMaxSize : function(){
50856          var cmax = this.config.maxSize || 10000;
50857          var center = this.mgr.getRegion("center");
50858          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50859     },
50860
50861     getVMaxSize : function(){
50862          var cmax = this.config.maxSize || 10000;
50863          var center = this.mgr.getRegion("center");
50864          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50865     },
50866
50867     onSplitMove : function(split, newSize){
50868         this.fireEvent("resized", this, newSize);
50869     },
50870     
50871     /** 
50872      * Returns the {@link Roo.SplitBar} for this region.
50873      * @return {Roo.SplitBar}
50874      */
50875     getSplitBar : function(){
50876         return this.split;
50877     },
50878     
50879     hide : function(){
50880         this.hideSplitter();
50881         Roo.SplitLayoutRegion.superclass.hide.call(this);
50882     },
50883
50884     hideSplitter : function(){
50885         if(this.split){
50886             this.split.el.setLocation(-2000,-2000);
50887             this.split.el.hide();
50888         }
50889     },
50890
50891     show : function(){
50892         if(this.split){
50893             this.split.el.show();
50894         }
50895         Roo.SplitLayoutRegion.superclass.show.call(this);
50896     },
50897     
50898     beforeSlide: function(){
50899         if(Roo.isGecko){// firefox overflow auto bug workaround
50900             this.bodyEl.clip();
50901             if(this.tabs) this.tabs.bodyEl.clip();
50902             if(this.activePanel){
50903                 this.activePanel.getEl().clip();
50904                 
50905                 if(this.activePanel.beforeSlide){
50906                     this.activePanel.beforeSlide();
50907                 }
50908             }
50909         }
50910     },
50911     
50912     afterSlide : function(){
50913         if(Roo.isGecko){// firefox overflow auto bug workaround
50914             this.bodyEl.unclip();
50915             if(this.tabs) this.tabs.bodyEl.unclip();
50916             if(this.activePanel){
50917                 this.activePanel.getEl().unclip();
50918                 if(this.activePanel.afterSlide){
50919                     this.activePanel.afterSlide();
50920                 }
50921             }
50922         }
50923     },
50924
50925     initAutoHide : function(){
50926         if(this.autoHide !== false){
50927             if(!this.autoHideHd){
50928                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50929                 this.autoHideHd = {
50930                     "mouseout": function(e){
50931                         if(!e.within(this.el, true)){
50932                             st.delay(500);
50933                         }
50934                     },
50935                     "mouseover" : function(e){
50936                         st.cancel();
50937                     },
50938                     scope : this
50939                 };
50940             }
50941             this.el.on(this.autoHideHd);
50942         }
50943     },
50944
50945     clearAutoHide : function(){
50946         if(this.autoHide !== false){
50947             this.el.un("mouseout", this.autoHideHd.mouseout);
50948             this.el.un("mouseover", this.autoHideHd.mouseover);
50949         }
50950     },
50951
50952     clearMonitor : function(){
50953         Roo.get(document).un("click", this.slideInIf, this);
50954     },
50955
50956     // these names are backwards but not changed for compat
50957     slideOut : function(){
50958         if(this.isSlid || this.el.hasActiveFx()){
50959             return;
50960         }
50961         this.isSlid = true;
50962         if(this.collapseBtn){
50963             this.collapseBtn.hide();
50964         }
50965         this.closeBtnState = this.closeBtn.getStyle('display');
50966         this.closeBtn.hide();
50967         if(this.stickBtn){
50968             this.stickBtn.show();
50969         }
50970         this.el.show();
50971         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50972         this.beforeSlide();
50973         this.el.setStyle("z-index", 10001);
50974         this.el.slideIn(this.getSlideAnchor(), {
50975             callback: function(){
50976                 this.afterSlide();
50977                 this.initAutoHide();
50978                 Roo.get(document).on("click", this.slideInIf, this);
50979                 this.fireEvent("slideshow", this);
50980             },
50981             scope: this,
50982             block: true
50983         });
50984     },
50985
50986     afterSlideIn : function(){
50987         this.clearAutoHide();
50988         this.isSlid = false;
50989         this.clearMonitor();
50990         this.el.setStyle("z-index", "");
50991         if(this.collapseBtn){
50992             this.collapseBtn.show();
50993         }
50994         this.closeBtn.setStyle('display', this.closeBtnState);
50995         if(this.stickBtn){
50996             this.stickBtn.hide();
50997         }
50998         this.fireEvent("slidehide", this);
50999     },
51000
51001     slideIn : function(cb){
51002         if(!this.isSlid || this.el.hasActiveFx()){
51003             Roo.callback(cb);
51004             return;
51005         }
51006         this.isSlid = false;
51007         this.beforeSlide();
51008         this.el.slideOut(this.getSlideAnchor(), {
51009             callback: function(){
51010                 this.el.setLeftTop(-10000, -10000);
51011                 this.afterSlide();
51012                 this.afterSlideIn();
51013                 Roo.callback(cb);
51014             },
51015             scope: this,
51016             block: true
51017         });
51018     },
51019     
51020     slideInIf : function(e){
51021         if(!e.within(this.el)){
51022             this.slideIn();
51023         }
51024     },
51025
51026     animateCollapse : function(){
51027         this.beforeSlide();
51028         this.el.setStyle("z-index", 20000);
51029         var anchor = this.getSlideAnchor();
51030         this.el.slideOut(anchor, {
51031             callback : function(){
51032                 this.el.setStyle("z-index", "");
51033                 this.collapsedEl.slideIn(anchor, {duration:.3});
51034                 this.afterSlide();
51035                 this.el.setLocation(-10000,-10000);
51036                 this.el.hide();
51037                 this.fireEvent("collapsed", this);
51038             },
51039             scope: this,
51040             block: true
51041         });
51042     },
51043
51044     animateExpand : function(){
51045         this.beforeSlide();
51046         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51047         this.el.setStyle("z-index", 20000);
51048         this.collapsedEl.hide({
51049             duration:.1
51050         });
51051         this.el.slideIn(this.getSlideAnchor(), {
51052             callback : function(){
51053                 this.el.setStyle("z-index", "");
51054                 this.afterSlide();
51055                 if(this.split){
51056                     this.split.el.show();
51057                 }
51058                 this.fireEvent("invalidated", this);
51059                 this.fireEvent("expanded", this);
51060             },
51061             scope: this,
51062             block: true
51063         });
51064     },
51065
51066     anchors : {
51067         "west" : "left",
51068         "east" : "right",
51069         "north" : "top",
51070         "south" : "bottom"
51071     },
51072
51073     sanchors : {
51074         "west" : "l",
51075         "east" : "r",
51076         "north" : "t",
51077         "south" : "b"
51078     },
51079
51080     canchors : {
51081         "west" : "tl-tr",
51082         "east" : "tr-tl",
51083         "north" : "tl-bl",
51084         "south" : "bl-tl"
51085     },
51086
51087     getAnchor : function(){
51088         return this.anchors[this.position];
51089     },
51090
51091     getCollapseAnchor : function(){
51092         return this.canchors[this.position];
51093     },
51094
51095     getSlideAnchor : function(){
51096         return this.sanchors[this.position];
51097     },
51098
51099     getAlignAdj : function(){
51100         var cm = this.cmargins;
51101         switch(this.position){
51102             case "west":
51103                 return [0, 0];
51104             break;
51105             case "east":
51106                 return [0, 0];
51107             break;
51108             case "north":
51109                 return [0, 0];
51110             break;
51111             case "south":
51112                 return [0, 0];
51113             break;
51114         }
51115     },
51116
51117     getExpandAdj : function(){
51118         var c = this.collapsedEl, cm = this.cmargins;
51119         switch(this.position){
51120             case "west":
51121                 return [-(cm.right+c.getWidth()+cm.left), 0];
51122             break;
51123             case "east":
51124                 return [cm.right+c.getWidth()+cm.left, 0];
51125             break;
51126             case "north":
51127                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51128             break;
51129             case "south":
51130                 return [0, cm.top+cm.bottom+c.getHeight()];
51131             break;
51132         }
51133     }
51134 });/*
51135  * Based on:
51136  * Ext JS Library 1.1.1
51137  * Copyright(c) 2006-2007, Ext JS, LLC.
51138  *
51139  * Originally Released Under LGPL - original licence link has changed is not relivant.
51140  *
51141  * Fork - LGPL
51142  * <script type="text/javascript">
51143  */
51144 /*
51145  * These classes are private internal classes
51146  */
51147 Roo.CenterLayoutRegion = function(mgr, config){
51148     Roo.LayoutRegion.call(this, mgr, config, "center");
51149     this.visible = true;
51150     this.minWidth = config.minWidth || 20;
51151     this.minHeight = config.minHeight || 20;
51152 };
51153
51154 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51155     hide : function(){
51156         // center panel can't be hidden
51157     },
51158     
51159     show : function(){
51160         // center panel can't be hidden
51161     },
51162     
51163     getMinWidth: function(){
51164         return this.minWidth;
51165     },
51166     
51167     getMinHeight: function(){
51168         return this.minHeight;
51169     }
51170 });
51171
51172
51173 Roo.NorthLayoutRegion = function(mgr, config){
51174     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51175     if(this.split){
51176         this.split.placement = Roo.SplitBar.TOP;
51177         this.split.orientation = Roo.SplitBar.VERTICAL;
51178         this.split.el.addClass("x-layout-split-v");
51179     }
51180     var size = config.initialSize || config.height;
51181     if(typeof size != "undefined"){
51182         this.el.setHeight(size);
51183     }
51184 };
51185 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51186     orientation: Roo.SplitBar.VERTICAL,
51187     getBox : function(){
51188         if(this.collapsed){
51189             return this.collapsedEl.getBox();
51190         }
51191         var box = this.el.getBox();
51192         if(this.split){
51193             box.height += this.split.el.getHeight();
51194         }
51195         return box;
51196     },
51197     
51198     updateBox : function(box){
51199         if(this.split && !this.collapsed){
51200             box.height -= this.split.el.getHeight();
51201             this.split.el.setLeft(box.x);
51202             this.split.el.setTop(box.y+box.height);
51203             this.split.el.setWidth(box.width);
51204         }
51205         if(this.collapsed){
51206             this.updateBody(box.width, null);
51207         }
51208         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51209     }
51210 });
51211
51212 Roo.SouthLayoutRegion = function(mgr, config){
51213     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51214     if(this.split){
51215         this.split.placement = Roo.SplitBar.BOTTOM;
51216         this.split.orientation = Roo.SplitBar.VERTICAL;
51217         this.split.el.addClass("x-layout-split-v");
51218     }
51219     var size = config.initialSize || config.height;
51220     if(typeof size != "undefined"){
51221         this.el.setHeight(size);
51222     }
51223 };
51224 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51225     orientation: Roo.SplitBar.VERTICAL,
51226     getBox : function(){
51227         if(this.collapsed){
51228             return this.collapsedEl.getBox();
51229         }
51230         var box = this.el.getBox();
51231         if(this.split){
51232             var sh = this.split.el.getHeight();
51233             box.height += sh;
51234             box.y -= sh;
51235         }
51236         return box;
51237     },
51238     
51239     updateBox : function(box){
51240         if(this.split && !this.collapsed){
51241             var sh = this.split.el.getHeight();
51242             box.height -= sh;
51243             box.y += sh;
51244             this.split.el.setLeft(box.x);
51245             this.split.el.setTop(box.y-sh);
51246             this.split.el.setWidth(box.width);
51247         }
51248         if(this.collapsed){
51249             this.updateBody(box.width, null);
51250         }
51251         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51252     }
51253 });
51254
51255 Roo.EastLayoutRegion = function(mgr, config){
51256     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51257     if(this.split){
51258         this.split.placement = Roo.SplitBar.RIGHT;
51259         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51260         this.split.el.addClass("x-layout-split-h");
51261     }
51262     var size = config.initialSize || config.width;
51263     if(typeof size != "undefined"){
51264         this.el.setWidth(size);
51265     }
51266 };
51267 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51268     orientation: Roo.SplitBar.HORIZONTAL,
51269     getBox : function(){
51270         if(this.collapsed){
51271             return this.collapsedEl.getBox();
51272         }
51273         var box = this.el.getBox();
51274         if(this.split){
51275             var sw = this.split.el.getWidth();
51276             box.width += sw;
51277             box.x -= sw;
51278         }
51279         return box;
51280     },
51281
51282     updateBox : function(box){
51283         if(this.split && !this.collapsed){
51284             var sw = this.split.el.getWidth();
51285             box.width -= sw;
51286             this.split.el.setLeft(box.x);
51287             this.split.el.setTop(box.y);
51288             this.split.el.setHeight(box.height);
51289             box.x += sw;
51290         }
51291         if(this.collapsed){
51292             this.updateBody(null, box.height);
51293         }
51294         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51295     }
51296 });
51297
51298 Roo.WestLayoutRegion = function(mgr, config){
51299     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51300     if(this.split){
51301         this.split.placement = Roo.SplitBar.LEFT;
51302         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51303         this.split.el.addClass("x-layout-split-h");
51304     }
51305     var size = config.initialSize || config.width;
51306     if(typeof size != "undefined"){
51307         this.el.setWidth(size);
51308     }
51309 };
51310 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51311     orientation: Roo.SplitBar.HORIZONTAL,
51312     getBox : function(){
51313         if(this.collapsed){
51314             return this.collapsedEl.getBox();
51315         }
51316         var box = this.el.getBox();
51317         if(this.split){
51318             box.width += this.split.el.getWidth();
51319         }
51320         return box;
51321     },
51322     
51323     updateBox : function(box){
51324         if(this.split && !this.collapsed){
51325             var sw = this.split.el.getWidth();
51326             box.width -= sw;
51327             this.split.el.setLeft(box.x+box.width);
51328             this.split.el.setTop(box.y);
51329             this.split.el.setHeight(box.height);
51330         }
51331         if(this.collapsed){
51332             this.updateBody(null, box.height);
51333         }
51334         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51335     }
51336 });
51337 /*
51338  * Based on:
51339  * Ext JS Library 1.1.1
51340  * Copyright(c) 2006-2007, Ext JS, LLC.
51341  *
51342  * Originally Released Under LGPL - original licence link has changed is not relivant.
51343  *
51344  * Fork - LGPL
51345  * <script type="text/javascript">
51346  */
51347  
51348  
51349 /*
51350  * Private internal class for reading and applying state
51351  */
51352 Roo.LayoutStateManager = function(layout){
51353      // default empty state
51354      this.state = {
51355         north: {},
51356         south: {},
51357         east: {},
51358         west: {}       
51359     };
51360 };
51361
51362 Roo.LayoutStateManager.prototype = {
51363     init : function(layout, provider){
51364         this.provider = provider;
51365         var state = provider.get(layout.id+"-layout-state");
51366         if(state){
51367             var wasUpdating = layout.isUpdating();
51368             if(!wasUpdating){
51369                 layout.beginUpdate();
51370             }
51371             for(var key in state){
51372                 if(typeof state[key] != "function"){
51373                     var rstate = state[key];
51374                     var r = layout.getRegion(key);
51375                     if(r && rstate){
51376                         if(rstate.size){
51377                             r.resizeTo(rstate.size);
51378                         }
51379                         if(rstate.collapsed == true){
51380                             r.collapse(true);
51381                         }else{
51382                             r.expand(null, true);
51383                         }
51384                     }
51385                 }
51386             }
51387             if(!wasUpdating){
51388                 layout.endUpdate();
51389             }
51390             this.state = state; 
51391         }
51392         this.layout = layout;
51393         layout.on("regionresized", this.onRegionResized, this);
51394         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51395         layout.on("regionexpanded", this.onRegionExpanded, this);
51396     },
51397     
51398     storeState : function(){
51399         this.provider.set(this.layout.id+"-layout-state", this.state);
51400     },
51401     
51402     onRegionResized : function(region, newSize){
51403         this.state[region.getPosition()].size = newSize;
51404         this.storeState();
51405     },
51406     
51407     onRegionCollapsed : function(region){
51408         this.state[region.getPosition()].collapsed = true;
51409         this.storeState();
51410     },
51411     
51412     onRegionExpanded : function(region){
51413         this.state[region.getPosition()].collapsed = false;
51414         this.storeState();
51415     }
51416 };/*
51417  * Based on:
51418  * Ext JS Library 1.1.1
51419  * Copyright(c) 2006-2007, Ext JS, LLC.
51420  *
51421  * Originally Released Under LGPL - original licence link has changed is not relivant.
51422  *
51423  * Fork - LGPL
51424  * <script type="text/javascript">
51425  */
51426 /**
51427  * @class Roo.ContentPanel
51428  * @extends Roo.util.Observable
51429  * A basic ContentPanel element.
51430  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51431  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51432  * @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
51433  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51434  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51435  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51436  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51437  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51438  * @cfg {String} title          The title for this panel
51439  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51440  * @cfg {String} url            Calls {@link #setUrl} with this value
51441  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51442  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51443  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51444  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51445
51446  * @constructor
51447  * Create a new ContentPanel.
51448  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51449  * @param {String/Object} config A string to set only the title or a config object
51450  * @param {String} content (optional) Set the HTML content for this panel
51451  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51452  */
51453 Roo.ContentPanel = function(el, config, content){
51454     
51455      
51456     /*
51457     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51458         config = el;
51459         el = Roo.id();
51460     }
51461     if (config && config.parentLayout) { 
51462         el = config.parentLayout.el.createChild(); 
51463     }
51464     */
51465     if(el.autoCreate){ // xtype is available if this is called from factory
51466         config = el;
51467         el = Roo.id();
51468     }
51469     this.el = Roo.get(el);
51470     if(!this.el && config && config.autoCreate){
51471         if(typeof config.autoCreate == "object"){
51472             if(!config.autoCreate.id){
51473                 config.autoCreate.id = config.id||el;
51474             }
51475             this.el = Roo.DomHelper.append(document.body,
51476                         config.autoCreate, true);
51477         }else{
51478             this.el = Roo.DomHelper.append(document.body,
51479                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51480         }
51481     }
51482     this.closable = false;
51483     this.loaded = false;
51484     this.active = false;
51485     if(typeof config == "string"){
51486         this.title = config;
51487     }else{
51488         Roo.apply(this, config);
51489     }
51490     
51491     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51492         this.wrapEl = this.el.wrap();
51493         this.toolbar.container = this.el.insertSibling(false, 'before');
51494         this.toolbar = new Roo.Toolbar(this.toolbar);
51495     }
51496     
51497     // xtype created footer. - not sure if will work as we normally have to render first..
51498     if (this.footer && !this.footer.el && this.footer.xtype) {
51499         if (!this.wrapEl) {
51500             this.wrapEl = this.el.wrap();
51501         }
51502     
51503         this.footer.container = this.wrapEl.createChild();
51504          
51505         this.footer = Roo.factory(this.footer, Roo);
51506         
51507     }
51508     
51509     if(this.resizeEl){
51510         this.resizeEl = Roo.get(this.resizeEl, true);
51511     }else{
51512         this.resizeEl = this.el;
51513     }
51514     // handle view.xtype
51515     
51516  
51517     
51518     
51519     this.addEvents({
51520         /**
51521          * @event activate
51522          * Fires when this panel is activated. 
51523          * @param {Roo.ContentPanel} this
51524          */
51525         "activate" : true,
51526         /**
51527          * @event deactivate
51528          * Fires when this panel is activated. 
51529          * @param {Roo.ContentPanel} this
51530          */
51531         "deactivate" : true,
51532
51533         /**
51534          * @event resize
51535          * Fires when this panel is resized if fitToFrame is true.
51536          * @param {Roo.ContentPanel} this
51537          * @param {Number} width The width after any component adjustments
51538          * @param {Number} height The height after any component adjustments
51539          */
51540         "resize" : true,
51541         
51542          /**
51543          * @event render
51544          * Fires when this tab is created
51545          * @param {Roo.ContentPanel} this
51546          */
51547         "render" : true
51548         
51549         
51550         
51551     });
51552     
51553
51554     
51555     
51556     if(this.autoScroll){
51557         this.resizeEl.setStyle("overflow", "auto");
51558     } else {
51559         // fix randome scrolling
51560         this.el.on('scroll', function() {
51561             Roo.log('fix random scolling');
51562             this.scrollTo('top',0); 
51563         });
51564     }
51565     content = content || this.content;
51566     if(content){
51567         this.setContent(content);
51568     }
51569     if(config && config.url){
51570         this.setUrl(this.url, this.params, this.loadOnce);
51571     }
51572     
51573     
51574     
51575     Roo.ContentPanel.superclass.constructor.call(this);
51576     
51577     if (this.view && typeof(this.view.xtype) != 'undefined') {
51578         this.view.el = this.el.appendChild(document.createElement("div"));
51579         this.view = Roo.factory(this.view); 
51580         this.view.render  &&  this.view.render(false, '');  
51581     }
51582     
51583     
51584     this.fireEvent('render', this);
51585 };
51586
51587 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51588     tabTip:'',
51589     setRegion : function(region){
51590         this.region = region;
51591         if(region){
51592            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51593         }else{
51594            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51595         } 
51596     },
51597     
51598     /**
51599      * Returns the toolbar for this Panel if one was configured. 
51600      * @return {Roo.Toolbar} 
51601      */
51602     getToolbar : function(){
51603         return this.toolbar;
51604     },
51605     
51606     setActiveState : function(active){
51607         this.active = active;
51608         if(!active){
51609             this.fireEvent("deactivate", this);
51610         }else{
51611             this.fireEvent("activate", this);
51612         }
51613     },
51614     /**
51615      * Updates this panel's element
51616      * @param {String} content The new content
51617      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51618     */
51619     setContent : function(content, loadScripts){
51620         this.el.update(content, loadScripts);
51621     },
51622
51623     ignoreResize : function(w, h){
51624         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51625             return true;
51626         }else{
51627             this.lastSize = {width: w, height: h};
51628             return false;
51629         }
51630     },
51631     /**
51632      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51633      * @return {Roo.UpdateManager} The UpdateManager
51634      */
51635     getUpdateManager : function(){
51636         return this.el.getUpdateManager();
51637     },
51638      /**
51639      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51640      * @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:
51641 <pre><code>
51642 panel.load({
51643     url: "your-url.php",
51644     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51645     callback: yourFunction,
51646     scope: yourObject, //(optional scope)
51647     discardUrl: false,
51648     nocache: false,
51649     text: "Loading...",
51650     timeout: 30,
51651     scripts: false
51652 });
51653 </code></pre>
51654      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51655      * 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.
51656      * @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}
51657      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51658      * @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.
51659      * @return {Roo.ContentPanel} this
51660      */
51661     load : function(){
51662         var um = this.el.getUpdateManager();
51663         um.update.apply(um, arguments);
51664         return this;
51665     },
51666
51667
51668     /**
51669      * 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.
51670      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51671      * @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)
51672      * @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)
51673      * @return {Roo.UpdateManager} The UpdateManager
51674      */
51675     setUrl : function(url, params, loadOnce){
51676         if(this.refreshDelegate){
51677             this.removeListener("activate", this.refreshDelegate);
51678         }
51679         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51680         this.on("activate", this.refreshDelegate);
51681         return this.el.getUpdateManager();
51682     },
51683     
51684     _handleRefresh : function(url, params, loadOnce){
51685         if(!loadOnce || !this.loaded){
51686             var updater = this.el.getUpdateManager();
51687             updater.update(url, params, this._setLoaded.createDelegate(this));
51688         }
51689     },
51690     
51691     _setLoaded : function(){
51692         this.loaded = true;
51693     }, 
51694     
51695     /**
51696      * Returns this panel's id
51697      * @return {String} 
51698      */
51699     getId : function(){
51700         return this.el.id;
51701     },
51702     
51703     /** 
51704      * Returns this panel's element - used by regiosn to add.
51705      * @return {Roo.Element} 
51706      */
51707     getEl : function(){
51708         return this.wrapEl || this.el;
51709     },
51710     
51711     adjustForComponents : function(width, height)
51712     {
51713         //Roo.log('adjustForComponents ');
51714         if(this.resizeEl != this.el){
51715             width -= this.el.getFrameWidth('lr');
51716             height -= this.el.getFrameWidth('tb');
51717         }
51718         if(this.toolbar){
51719             var te = this.toolbar.getEl();
51720             height -= te.getHeight();
51721             te.setWidth(width);
51722         }
51723         if(this.footer){
51724             var te = this.footer.getEl();
51725             Roo.log("footer:" + te.getHeight());
51726             
51727             height -= te.getHeight();
51728             te.setWidth(width);
51729         }
51730         
51731         
51732         if(this.adjustments){
51733             width += this.adjustments[0];
51734             height += this.adjustments[1];
51735         }
51736         return {"width": width, "height": height};
51737     },
51738     
51739     setSize : function(width, height){
51740         if(this.fitToFrame && !this.ignoreResize(width, height)){
51741             if(this.fitContainer && this.resizeEl != this.el){
51742                 this.el.setSize(width, height);
51743             }
51744             var size = this.adjustForComponents(width, height);
51745             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51746             this.fireEvent('resize', this, size.width, size.height);
51747         }
51748     },
51749     
51750     /**
51751      * Returns this panel's title
51752      * @return {String} 
51753      */
51754     getTitle : function(){
51755         return this.title;
51756     },
51757     
51758     /**
51759      * Set this panel's title
51760      * @param {String} title
51761      */
51762     setTitle : function(title){
51763         this.title = title;
51764         if(this.region){
51765             this.region.updatePanelTitle(this, title);
51766         }
51767     },
51768     
51769     /**
51770      * Returns true is this panel was configured to be closable
51771      * @return {Boolean} 
51772      */
51773     isClosable : function(){
51774         return this.closable;
51775     },
51776     
51777     beforeSlide : function(){
51778         this.el.clip();
51779         this.resizeEl.clip();
51780     },
51781     
51782     afterSlide : function(){
51783         this.el.unclip();
51784         this.resizeEl.unclip();
51785     },
51786     
51787     /**
51788      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51789      *   Will fail silently if the {@link #setUrl} method has not been called.
51790      *   This does not activate the panel, just updates its content.
51791      */
51792     refresh : function(){
51793         if(this.refreshDelegate){
51794            this.loaded = false;
51795            this.refreshDelegate();
51796         }
51797     },
51798     
51799     /**
51800      * Destroys this panel
51801      */
51802     destroy : function(){
51803         this.el.removeAllListeners();
51804         var tempEl = document.createElement("span");
51805         tempEl.appendChild(this.el.dom);
51806         tempEl.innerHTML = "";
51807         this.el.remove();
51808         this.el = null;
51809     },
51810     
51811     /**
51812      * form - if the content panel contains a form - this is a reference to it.
51813      * @type {Roo.form.Form}
51814      */
51815     form : false,
51816     /**
51817      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51818      *    This contains a reference to it.
51819      * @type {Roo.View}
51820      */
51821     view : false,
51822     
51823       /**
51824      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51825      * <pre><code>
51826
51827 layout.addxtype({
51828        xtype : 'Form',
51829        items: [ .... ]
51830    }
51831 );
51832
51833 </code></pre>
51834      * @param {Object} cfg Xtype definition of item to add.
51835      */
51836     
51837     addxtype : function(cfg) {
51838         // add form..
51839         if (cfg.xtype.match(/^Form$/)) {
51840             
51841             var el;
51842             //if (this.footer) {
51843             //    el = this.footer.container.insertSibling(false, 'before');
51844             //} else {
51845                 el = this.el.createChild();
51846             //}
51847
51848             this.form = new  Roo.form.Form(cfg);
51849             
51850             
51851             if ( this.form.allItems.length) this.form.render(el.dom);
51852             return this.form;
51853         }
51854         // should only have one of theses..
51855         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51856             // views.. should not be just added - used named prop 'view''
51857             
51858             cfg.el = this.el.appendChild(document.createElement("div"));
51859             // factory?
51860             
51861             var ret = new Roo.factory(cfg);
51862              
51863              ret.render && ret.render(false, ''); // render blank..
51864             this.view = ret;
51865             return ret;
51866         }
51867         return false;
51868     }
51869 });
51870
51871 /**
51872  * @class Roo.GridPanel
51873  * @extends Roo.ContentPanel
51874  * @constructor
51875  * Create a new GridPanel.
51876  * @param {Roo.grid.Grid} grid The grid for this panel
51877  * @param {String/Object} config A string to set only the panel's title, or a config object
51878  */
51879 Roo.GridPanel = function(grid, config){
51880     
51881   
51882     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51883         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51884         
51885     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51886     
51887     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51888     
51889     if(this.toolbar){
51890         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51891     }
51892     // xtype created footer. - not sure if will work as we normally have to render first..
51893     if (this.footer && !this.footer.el && this.footer.xtype) {
51894         
51895         this.footer.container = this.grid.getView().getFooterPanel(true);
51896         this.footer.dataSource = this.grid.dataSource;
51897         this.footer = Roo.factory(this.footer, Roo);
51898         
51899     }
51900     
51901     grid.monitorWindowResize = false; // turn off autosizing
51902     grid.autoHeight = false;
51903     grid.autoWidth = false;
51904     this.grid = grid;
51905     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51906 };
51907
51908 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51909     getId : function(){
51910         return this.grid.id;
51911     },
51912     
51913     /**
51914      * Returns the grid for this panel
51915      * @return {Roo.grid.Grid} 
51916      */
51917     getGrid : function(){
51918         return this.grid;    
51919     },
51920     
51921     setSize : function(width, height){
51922         if(!this.ignoreResize(width, height)){
51923             var grid = this.grid;
51924             var size = this.adjustForComponents(width, height);
51925             grid.getGridEl().setSize(size.width, size.height);
51926             grid.autoSize();
51927         }
51928     },
51929     
51930     beforeSlide : function(){
51931         this.grid.getView().scroller.clip();
51932     },
51933     
51934     afterSlide : function(){
51935         this.grid.getView().scroller.unclip();
51936     },
51937     
51938     destroy : function(){
51939         this.grid.destroy();
51940         delete this.grid;
51941         Roo.GridPanel.superclass.destroy.call(this); 
51942     }
51943 });
51944
51945
51946 /**
51947  * @class Roo.NestedLayoutPanel
51948  * @extends Roo.ContentPanel
51949  * @constructor
51950  * Create a new NestedLayoutPanel.
51951  * 
51952  * 
51953  * @param {Roo.BorderLayout} layout The layout for this panel
51954  * @param {String/Object} config A string to set only the title or a config object
51955  */
51956 Roo.NestedLayoutPanel = function(layout, config)
51957 {
51958     // construct with only one argument..
51959     /* FIXME - implement nicer consturctors
51960     if (layout.layout) {
51961         config = layout;
51962         layout = config.layout;
51963         delete config.layout;
51964     }
51965     if (layout.xtype && !layout.getEl) {
51966         // then layout needs constructing..
51967         layout = Roo.factory(layout, Roo);
51968     }
51969     */
51970     
51971     
51972     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51973     
51974     layout.monitorWindowResize = false; // turn off autosizing
51975     this.layout = layout;
51976     this.layout.getEl().addClass("x-layout-nested-layout");
51977     
51978     
51979     
51980     
51981 };
51982
51983 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51984
51985     setSize : function(width, height){
51986         if(!this.ignoreResize(width, height)){
51987             var size = this.adjustForComponents(width, height);
51988             var el = this.layout.getEl();
51989             el.setSize(size.width, size.height);
51990             var touch = el.dom.offsetWidth;
51991             this.layout.layout();
51992             // ie requires a double layout on the first pass
51993             if(Roo.isIE && !this.initialized){
51994                 this.initialized = true;
51995                 this.layout.layout();
51996             }
51997         }
51998     },
51999     
52000     // activate all subpanels if not currently active..
52001     
52002     setActiveState : function(active){
52003         this.active = active;
52004         if(!active){
52005             this.fireEvent("deactivate", this);
52006             return;
52007         }
52008         
52009         this.fireEvent("activate", this);
52010         // not sure if this should happen before or after..
52011         if (!this.layout) {
52012             return; // should not happen..
52013         }
52014         var reg = false;
52015         for (var r in this.layout.regions) {
52016             reg = this.layout.getRegion(r);
52017             if (reg.getActivePanel()) {
52018                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52019                 reg.setActivePanel(reg.getActivePanel());
52020                 continue;
52021             }
52022             if (!reg.panels.length) {
52023                 continue;
52024             }
52025             reg.showPanel(reg.getPanel(0));
52026         }
52027         
52028         
52029         
52030         
52031     },
52032     
52033     /**
52034      * Returns the nested BorderLayout for this panel
52035      * @return {Roo.BorderLayout} 
52036      */
52037     getLayout : function(){
52038         return this.layout;
52039     },
52040     
52041      /**
52042      * Adds a xtype elements to the layout of the nested panel
52043      * <pre><code>
52044
52045 panel.addxtype({
52046        xtype : 'ContentPanel',
52047        region: 'west',
52048        items: [ .... ]
52049    }
52050 );
52051
52052 panel.addxtype({
52053         xtype : 'NestedLayoutPanel',
52054         region: 'west',
52055         layout: {
52056            center: { },
52057            west: { }   
52058         },
52059         items : [ ... list of content panels or nested layout panels.. ]
52060    }
52061 );
52062 </code></pre>
52063      * @param {Object} cfg Xtype definition of item to add.
52064      */
52065     addxtype : function(cfg) {
52066         return this.layout.addxtype(cfg);
52067     
52068     }
52069 });
52070
52071 Roo.ScrollPanel = function(el, config, content){
52072     config = config || {};
52073     config.fitToFrame = true;
52074     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52075     
52076     this.el.dom.style.overflow = "hidden";
52077     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52078     this.el.removeClass("x-layout-inactive-content");
52079     this.el.on("mousewheel", this.onWheel, this);
52080
52081     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52082     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52083     up.unselectable(); down.unselectable();
52084     up.on("click", this.scrollUp, this);
52085     down.on("click", this.scrollDown, this);
52086     up.addClassOnOver("x-scroller-btn-over");
52087     down.addClassOnOver("x-scroller-btn-over");
52088     up.addClassOnClick("x-scroller-btn-click");
52089     down.addClassOnClick("x-scroller-btn-click");
52090     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52091
52092     this.resizeEl = this.el;
52093     this.el = wrap; this.up = up; this.down = down;
52094 };
52095
52096 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52097     increment : 100,
52098     wheelIncrement : 5,
52099     scrollUp : function(){
52100         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52101     },
52102
52103     scrollDown : function(){
52104         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52105     },
52106
52107     afterScroll : function(){
52108         var el = this.resizeEl;
52109         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52110         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52111         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52112     },
52113
52114     setSize : function(){
52115         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52116         this.afterScroll();
52117     },
52118
52119     onWheel : function(e){
52120         var d = e.getWheelDelta();
52121         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52122         this.afterScroll();
52123         e.stopEvent();
52124     },
52125
52126     setContent : function(content, loadScripts){
52127         this.resizeEl.update(content, loadScripts);
52128     }
52129
52130 });
52131
52132
52133
52134
52135
52136
52137
52138
52139
52140 /**
52141  * @class Roo.TreePanel
52142  * @extends Roo.ContentPanel
52143  * @constructor
52144  * Create a new TreePanel. - defaults to fit/scoll contents.
52145  * @param {String/Object} config A string to set only the panel's title, or a config object
52146  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52147  */
52148 Roo.TreePanel = function(config){
52149     var el = config.el;
52150     var tree = config.tree;
52151     delete config.tree; 
52152     delete config.el; // hopefull!
52153     
52154     // wrapper for IE7 strict & safari scroll issue
52155     
52156     var treeEl = el.createChild();
52157     config.resizeEl = treeEl;
52158     
52159     
52160     
52161     Roo.TreePanel.superclass.constructor.call(this, el, config);
52162  
52163  
52164     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52165     //console.log(tree);
52166     this.on('activate', function()
52167     {
52168         if (this.tree.rendered) {
52169             return;
52170         }
52171         //console.log('render tree');
52172         this.tree.render();
52173     });
52174     // this should not be needed.. - it's actually the 'el' that resizes?
52175     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52176     
52177     //this.on('resize',  function (cp, w, h) {
52178     //        this.tree.innerCt.setWidth(w);
52179     //        this.tree.innerCt.setHeight(h);
52180     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52181     //});
52182
52183         
52184     
52185 };
52186
52187 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52188     fitToFrame : true,
52189     autoScroll : true
52190 });
52191
52192
52193
52194
52195
52196
52197
52198
52199
52200
52201
52202 /*
52203  * Based on:
52204  * Ext JS Library 1.1.1
52205  * Copyright(c) 2006-2007, Ext JS, LLC.
52206  *
52207  * Originally Released Under LGPL - original licence link has changed is not relivant.
52208  *
52209  * Fork - LGPL
52210  * <script type="text/javascript">
52211  */
52212  
52213
52214 /**
52215  * @class Roo.ReaderLayout
52216  * @extends Roo.BorderLayout
52217  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52218  * center region containing two nested regions (a top one for a list view and one for item preview below),
52219  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52220  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52221  * expedites the setup of the overall layout and regions for this common application style.
52222  * Example:
52223  <pre><code>
52224 var reader = new Roo.ReaderLayout();
52225 var CP = Roo.ContentPanel;  // shortcut for adding
52226
52227 reader.beginUpdate();
52228 reader.add("north", new CP("north", "North"));
52229 reader.add("west", new CP("west", {title: "West"}));
52230 reader.add("east", new CP("east", {title: "East"}));
52231
52232 reader.regions.listView.add(new CP("listView", "List"));
52233 reader.regions.preview.add(new CP("preview", "Preview"));
52234 reader.endUpdate();
52235 </code></pre>
52236 * @constructor
52237 * Create a new ReaderLayout
52238 * @param {Object} config Configuration options
52239 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52240 * document.body if omitted)
52241 */
52242 Roo.ReaderLayout = function(config, renderTo){
52243     var c = config || {size:{}};
52244     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52245         north: c.north !== false ? Roo.apply({
52246             split:false,
52247             initialSize: 32,
52248             titlebar: false
52249         }, c.north) : false,
52250         west: c.west !== false ? Roo.apply({
52251             split:true,
52252             initialSize: 200,
52253             minSize: 175,
52254             maxSize: 400,
52255             titlebar: true,
52256             collapsible: true,
52257             animate: true,
52258             margins:{left:5,right:0,bottom:5,top:5},
52259             cmargins:{left:5,right:5,bottom:5,top:5}
52260         }, c.west) : false,
52261         east: c.east !== false ? Roo.apply({
52262             split:true,
52263             initialSize: 200,
52264             minSize: 175,
52265             maxSize: 400,
52266             titlebar: true,
52267             collapsible: true,
52268             animate: true,
52269             margins:{left:0,right:5,bottom:5,top:5},
52270             cmargins:{left:5,right:5,bottom:5,top:5}
52271         }, c.east) : false,
52272         center: Roo.apply({
52273             tabPosition: 'top',
52274             autoScroll:false,
52275             closeOnTab: true,
52276             titlebar:false,
52277             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52278         }, c.center)
52279     });
52280
52281     this.el.addClass('x-reader');
52282
52283     this.beginUpdate();
52284
52285     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52286         south: c.preview !== false ? Roo.apply({
52287             split:true,
52288             initialSize: 200,
52289             minSize: 100,
52290             autoScroll:true,
52291             collapsible:true,
52292             titlebar: true,
52293             cmargins:{top:5,left:0, right:0, bottom:0}
52294         }, c.preview) : false,
52295         center: Roo.apply({
52296             autoScroll:false,
52297             titlebar:false,
52298             minHeight:200
52299         }, c.listView)
52300     });
52301     this.add('center', new Roo.NestedLayoutPanel(inner,
52302             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52303
52304     this.endUpdate();
52305
52306     this.regions.preview = inner.getRegion('south');
52307     this.regions.listView = inner.getRegion('center');
52308 };
52309
52310 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52311  * Based on:
52312  * Ext JS Library 1.1.1
52313  * Copyright(c) 2006-2007, Ext JS, LLC.
52314  *
52315  * Originally Released Under LGPL - original licence link has changed is not relivant.
52316  *
52317  * Fork - LGPL
52318  * <script type="text/javascript">
52319  */
52320  
52321 /**
52322  * @class Roo.grid.Grid
52323  * @extends Roo.util.Observable
52324  * This class represents the primary interface of a component based grid control.
52325  * <br><br>Usage:<pre><code>
52326  var grid = new Roo.grid.Grid("my-container-id", {
52327      ds: myDataStore,
52328      cm: myColModel,
52329      selModel: mySelectionModel,
52330      autoSizeColumns: true,
52331      monitorWindowResize: false,
52332      trackMouseOver: true
52333  });
52334  // set any options
52335  grid.render();
52336  * </code></pre>
52337  * <b>Common Problems:</b><br/>
52338  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52339  * element will correct this<br/>
52340  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52341  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52342  * are unpredictable.<br/>
52343  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52344  * grid to calculate dimensions/offsets.<br/>
52345   * @constructor
52346  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52347  * The container MUST have some type of size defined for the grid to fill. The container will be
52348  * automatically set to position relative if it isn't already.
52349  * @param {Object} config A config object that sets properties on this grid.
52350  */
52351 Roo.grid.Grid = function(container, config){
52352         // initialize the container
52353         this.container = Roo.get(container);
52354         this.container.update("");
52355         this.container.setStyle("overflow", "hidden");
52356     this.container.addClass('x-grid-container');
52357
52358     this.id = this.container.id;
52359
52360     Roo.apply(this, config);
52361     // check and correct shorthanded configs
52362     if(this.ds){
52363         this.dataSource = this.ds;
52364         delete this.ds;
52365     }
52366     if(this.cm){
52367         this.colModel = this.cm;
52368         delete this.cm;
52369     }
52370     if(this.sm){
52371         this.selModel = this.sm;
52372         delete this.sm;
52373     }
52374
52375     if (this.selModel) {
52376         this.selModel = Roo.factory(this.selModel, Roo.grid);
52377         this.sm = this.selModel;
52378         this.sm.xmodule = this.xmodule || false;
52379     }
52380     if (typeof(this.colModel.config) == 'undefined') {
52381         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52382         this.cm = this.colModel;
52383         this.cm.xmodule = this.xmodule || false;
52384     }
52385     if (this.dataSource) {
52386         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52387         this.ds = this.dataSource;
52388         this.ds.xmodule = this.xmodule || false;
52389          
52390     }
52391     
52392     
52393     
52394     if(this.width){
52395         this.container.setWidth(this.width);
52396     }
52397
52398     if(this.height){
52399         this.container.setHeight(this.height);
52400     }
52401     /** @private */
52402         this.addEvents({
52403         // raw events
52404         /**
52405          * @event click
52406          * The raw click event for the entire grid.
52407          * @param {Roo.EventObject} e
52408          */
52409         "click" : true,
52410         /**
52411          * @event dblclick
52412          * The raw dblclick event for the entire grid.
52413          * @param {Roo.EventObject} e
52414          */
52415         "dblclick" : true,
52416         /**
52417          * @event contextmenu
52418          * The raw contextmenu event for the entire grid.
52419          * @param {Roo.EventObject} e
52420          */
52421         "contextmenu" : true,
52422         /**
52423          * @event mousedown
52424          * The raw mousedown event for the entire grid.
52425          * @param {Roo.EventObject} e
52426          */
52427         "mousedown" : true,
52428         /**
52429          * @event mouseup
52430          * The raw mouseup event for the entire grid.
52431          * @param {Roo.EventObject} e
52432          */
52433         "mouseup" : true,
52434         /**
52435          * @event mouseover
52436          * The raw mouseover event for the entire grid.
52437          * @param {Roo.EventObject} e
52438          */
52439         "mouseover" : true,
52440         /**
52441          * @event mouseout
52442          * The raw mouseout event for the entire grid.
52443          * @param {Roo.EventObject} e
52444          */
52445         "mouseout" : true,
52446         /**
52447          * @event keypress
52448          * The raw keypress event for the entire grid.
52449          * @param {Roo.EventObject} e
52450          */
52451         "keypress" : true,
52452         /**
52453          * @event keydown
52454          * The raw keydown event for the entire grid.
52455          * @param {Roo.EventObject} e
52456          */
52457         "keydown" : true,
52458
52459         // custom events
52460
52461         /**
52462          * @event cellclick
52463          * Fires when a cell is clicked
52464          * @param {Grid} this
52465          * @param {Number} rowIndex
52466          * @param {Number} columnIndex
52467          * @param {Roo.EventObject} e
52468          */
52469         "cellclick" : true,
52470         /**
52471          * @event celldblclick
52472          * Fires when a cell is double clicked
52473          * @param {Grid} this
52474          * @param {Number} rowIndex
52475          * @param {Number} columnIndex
52476          * @param {Roo.EventObject} e
52477          */
52478         "celldblclick" : true,
52479         /**
52480          * @event rowclick
52481          * Fires when a row is clicked
52482          * @param {Grid} this
52483          * @param {Number} rowIndex
52484          * @param {Roo.EventObject} e
52485          */
52486         "rowclick" : true,
52487         /**
52488          * @event rowdblclick
52489          * Fires when a row is double clicked
52490          * @param {Grid} this
52491          * @param {Number} rowIndex
52492          * @param {Roo.EventObject} e
52493          */
52494         "rowdblclick" : true,
52495         /**
52496          * @event headerclick
52497          * Fires when a header is clicked
52498          * @param {Grid} this
52499          * @param {Number} columnIndex
52500          * @param {Roo.EventObject} e
52501          */
52502         "headerclick" : true,
52503         /**
52504          * @event headerdblclick
52505          * Fires when a header cell is double clicked
52506          * @param {Grid} this
52507          * @param {Number} columnIndex
52508          * @param {Roo.EventObject} e
52509          */
52510         "headerdblclick" : true,
52511         /**
52512          * @event rowcontextmenu
52513          * Fires when a row is right clicked
52514          * @param {Grid} this
52515          * @param {Number} rowIndex
52516          * @param {Roo.EventObject} e
52517          */
52518         "rowcontextmenu" : true,
52519         /**
52520          * @event cellcontextmenu
52521          * Fires when a cell is right clicked
52522          * @param {Grid} this
52523          * @param {Number} rowIndex
52524          * @param {Number} cellIndex
52525          * @param {Roo.EventObject} e
52526          */
52527          "cellcontextmenu" : true,
52528         /**
52529          * @event headercontextmenu
52530          * Fires when a header is right clicked
52531          * @param {Grid} this
52532          * @param {Number} columnIndex
52533          * @param {Roo.EventObject} e
52534          */
52535         "headercontextmenu" : true,
52536         /**
52537          * @event bodyscroll
52538          * Fires when the body element is scrolled
52539          * @param {Number} scrollLeft
52540          * @param {Number} scrollTop
52541          */
52542         "bodyscroll" : true,
52543         /**
52544          * @event columnresize
52545          * Fires when the user resizes a column
52546          * @param {Number} columnIndex
52547          * @param {Number} newSize
52548          */
52549         "columnresize" : true,
52550         /**
52551          * @event columnmove
52552          * Fires when the user moves a column
52553          * @param {Number} oldIndex
52554          * @param {Number} newIndex
52555          */
52556         "columnmove" : true,
52557         /**
52558          * @event startdrag
52559          * Fires when row(s) start being dragged
52560          * @param {Grid} this
52561          * @param {Roo.GridDD} dd The drag drop object
52562          * @param {event} e The raw browser event
52563          */
52564         "startdrag" : true,
52565         /**
52566          * @event enddrag
52567          * Fires when a drag operation is complete
52568          * @param {Grid} this
52569          * @param {Roo.GridDD} dd The drag drop object
52570          * @param {event} e The raw browser event
52571          */
52572         "enddrag" : true,
52573         /**
52574          * @event dragdrop
52575          * Fires when dragged row(s) are dropped on a valid DD target
52576          * @param {Grid} this
52577          * @param {Roo.GridDD} dd The drag drop object
52578          * @param {String} targetId The target drag drop object
52579          * @param {event} e The raw browser event
52580          */
52581         "dragdrop" : true,
52582         /**
52583          * @event dragover
52584          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52585          * @param {Grid} this
52586          * @param {Roo.GridDD} dd The drag drop object
52587          * @param {String} targetId The target drag drop object
52588          * @param {event} e The raw browser event
52589          */
52590         "dragover" : true,
52591         /**
52592          * @event dragenter
52593          *  Fires when the dragged row(s) first cross another DD target while being dragged
52594          * @param {Grid} this
52595          * @param {Roo.GridDD} dd The drag drop object
52596          * @param {String} targetId The target drag drop object
52597          * @param {event} e The raw browser event
52598          */
52599         "dragenter" : true,
52600         /**
52601          * @event dragout
52602          * Fires when the dragged row(s) leave another DD target while being dragged
52603          * @param {Grid} this
52604          * @param {Roo.GridDD} dd The drag drop object
52605          * @param {String} targetId The target drag drop object
52606          * @param {event} e The raw browser event
52607          */
52608         "dragout" : true,
52609         /**
52610          * @event rowclass
52611          * Fires when a row is rendered, so you can change add a style to it.
52612          * @param {GridView} gridview   The grid view
52613          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52614          */
52615         'rowclass' : true,
52616
52617         /**
52618          * @event render
52619          * Fires when the grid is rendered
52620          * @param {Grid} grid
52621          */
52622         'render' : true
52623     });
52624
52625     Roo.grid.Grid.superclass.constructor.call(this);
52626 };
52627 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52628     
52629     /**
52630      * @cfg {String} ddGroup - drag drop group.
52631      */
52632
52633     /**
52634      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52635      */
52636     minColumnWidth : 25,
52637
52638     /**
52639      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52640      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52641      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52642      */
52643     autoSizeColumns : false,
52644
52645     /**
52646      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52647      */
52648     autoSizeHeaders : true,
52649
52650     /**
52651      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52652      */
52653     monitorWindowResize : true,
52654
52655     /**
52656      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52657      * rows measured to get a columns size. Default is 0 (all rows).
52658      */
52659     maxRowsToMeasure : 0,
52660
52661     /**
52662      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52663      */
52664     trackMouseOver : true,
52665
52666     /**
52667     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52668     */
52669     
52670     /**
52671     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52672     */
52673     enableDragDrop : false,
52674     
52675     /**
52676     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52677     */
52678     enableColumnMove : true,
52679     
52680     /**
52681     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52682     */
52683     enableColumnHide : true,
52684     
52685     /**
52686     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52687     */
52688     enableRowHeightSync : false,
52689     
52690     /**
52691     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52692     */
52693     stripeRows : true,
52694     
52695     /**
52696     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52697     */
52698     autoHeight : false,
52699
52700     /**
52701      * @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.
52702      */
52703     autoExpandColumn : false,
52704
52705     /**
52706     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52707     * Default is 50.
52708     */
52709     autoExpandMin : 50,
52710
52711     /**
52712     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52713     */
52714     autoExpandMax : 1000,
52715
52716     /**
52717     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52718     */
52719     view : null,
52720
52721     /**
52722     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52723     */
52724     loadMask : false,
52725     /**
52726     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52727     */
52728     dropTarget: false,
52729     
52730    
52731     
52732     // private
52733     rendered : false,
52734
52735     /**
52736     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52737     * of a fixed width. Default is false.
52738     */
52739     /**
52740     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52741     */
52742     /**
52743      * Called once after all setup has been completed and the grid is ready to be rendered.
52744      * @return {Roo.grid.Grid} this
52745      */
52746     render : function()
52747     {
52748         var c = this.container;
52749         // try to detect autoHeight/width mode
52750         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52751             this.autoHeight = true;
52752         }
52753         var view = this.getView();
52754         view.init(this);
52755
52756         c.on("click", this.onClick, this);
52757         c.on("dblclick", this.onDblClick, this);
52758         c.on("contextmenu", this.onContextMenu, this);
52759         c.on("keydown", this.onKeyDown, this);
52760         if (Roo.isTouch) {
52761             c.on("touchstart", this.onTouchStart, this);
52762         }
52763
52764         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52765
52766         this.getSelectionModel().init(this);
52767
52768         view.render();
52769
52770         if(this.loadMask){
52771             this.loadMask = new Roo.LoadMask(this.container,
52772                     Roo.apply({store:this.dataSource}, this.loadMask));
52773         }
52774         
52775         
52776         if (this.toolbar && this.toolbar.xtype) {
52777             this.toolbar.container = this.getView().getHeaderPanel(true);
52778             this.toolbar = new Roo.Toolbar(this.toolbar);
52779         }
52780         if (this.footer && this.footer.xtype) {
52781             this.footer.dataSource = this.getDataSource();
52782             this.footer.container = this.getView().getFooterPanel(true);
52783             this.footer = Roo.factory(this.footer, Roo);
52784         }
52785         if (this.dropTarget && this.dropTarget.xtype) {
52786             delete this.dropTarget.xtype;
52787             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52788         }
52789         
52790         
52791         this.rendered = true;
52792         this.fireEvent('render', this);
52793         return this;
52794     },
52795
52796         /**
52797          * Reconfigures the grid to use a different Store and Column Model.
52798          * The View will be bound to the new objects and refreshed.
52799          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52800          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52801          */
52802     reconfigure : function(dataSource, colModel){
52803         if(this.loadMask){
52804             this.loadMask.destroy();
52805             this.loadMask = new Roo.LoadMask(this.container,
52806                     Roo.apply({store:dataSource}, this.loadMask));
52807         }
52808         this.view.bind(dataSource, colModel);
52809         this.dataSource = dataSource;
52810         this.colModel = colModel;
52811         this.view.refresh(true);
52812     },
52813
52814     // private
52815     onKeyDown : function(e){
52816         this.fireEvent("keydown", e);
52817     },
52818
52819     /**
52820      * Destroy this grid.
52821      * @param {Boolean} removeEl True to remove the element
52822      */
52823     destroy : function(removeEl, keepListeners){
52824         if(this.loadMask){
52825             this.loadMask.destroy();
52826         }
52827         var c = this.container;
52828         c.removeAllListeners();
52829         this.view.destroy();
52830         this.colModel.purgeListeners();
52831         if(!keepListeners){
52832             this.purgeListeners();
52833         }
52834         c.update("");
52835         if(removeEl === true){
52836             c.remove();
52837         }
52838     },
52839
52840     // private
52841     processEvent : function(name, e){
52842         // does this fire select???
52843         //Roo.log('grid:processEvent '  + name);
52844         
52845         if (name != 'touchstart' ) {
52846             this.fireEvent(name, e);    
52847         }
52848         
52849         var t = e.getTarget();
52850         var v = this.view;
52851         var header = v.findHeaderIndex(t);
52852         if(header !== false){
52853             var ename = name == 'touchstart' ? 'click' : name;
52854              
52855             this.fireEvent("header" + ename, this, header, e);
52856         }else{
52857             var row = v.findRowIndex(t);
52858             var cell = v.findCellIndex(t);
52859             if (name == 'touchstart') {
52860                 // first touch is always a click.
52861                 // hopefull this happens after selection is updated.?
52862                 name = false;
52863                 
52864                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52865                     var cs = this.selModel.getSelectedCell();
52866                     if (row == cs[0] && cell == cs[1]){
52867                         name = 'dblclick';
52868                     }
52869                 }
52870                 if (typeof(this.selModel.getSelections) != 'undefined') {
52871                     var cs = this.selModel.getSelections();
52872                     var ds = this.dataSource;
52873                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52874                         name = 'dblclick';
52875                     }
52876                 }
52877                 if (!name) {
52878                     return;
52879                 }
52880             }
52881             
52882             
52883             if(row !== false){
52884                 this.fireEvent("row" + name, this, row, e);
52885                 if(cell !== false){
52886                     this.fireEvent("cell" + name, this, row, cell, e);
52887                 }
52888             }
52889         }
52890     },
52891
52892     // private
52893     onClick : function(e){
52894         this.processEvent("click", e);
52895     },
52896    // private
52897     onTouchStart : function(e){
52898         this.processEvent("touchstart", e);
52899     },
52900
52901     // private
52902     onContextMenu : function(e, t){
52903         this.processEvent("contextmenu", e);
52904     },
52905
52906     // private
52907     onDblClick : function(e){
52908         this.processEvent("dblclick", e);
52909     },
52910
52911     // private
52912     walkCells : function(row, col, step, fn, scope){
52913         var cm = this.colModel, clen = cm.getColumnCount();
52914         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52915         if(step < 0){
52916             if(col < 0){
52917                 row--;
52918                 first = false;
52919             }
52920             while(row >= 0){
52921                 if(!first){
52922                     col = clen-1;
52923                 }
52924                 first = false;
52925                 while(col >= 0){
52926                     if(fn.call(scope || this, row, col, cm) === true){
52927                         return [row, col];
52928                     }
52929                     col--;
52930                 }
52931                 row--;
52932             }
52933         } else {
52934             if(col >= clen){
52935                 row++;
52936                 first = false;
52937             }
52938             while(row < rlen){
52939                 if(!first){
52940                     col = 0;
52941                 }
52942                 first = false;
52943                 while(col < clen){
52944                     if(fn.call(scope || this, row, col, cm) === true){
52945                         return [row, col];
52946                     }
52947                     col++;
52948                 }
52949                 row++;
52950             }
52951         }
52952         return null;
52953     },
52954
52955     // private
52956     getSelections : function(){
52957         return this.selModel.getSelections();
52958     },
52959
52960     /**
52961      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52962      * but if manual update is required this method will initiate it.
52963      */
52964     autoSize : function(){
52965         if(this.rendered){
52966             this.view.layout();
52967             if(this.view.adjustForScroll){
52968                 this.view.adjustForScroll();
52969             }
52970         }
52971     },
52972
52973     /**
52974      * Returns the grid's underlying element.
52975      * @return {Element} The element
52976      */
52977     getGridEl : function(){
52978         return this.container;
52979     },
52980
52981     // private for compatibility, overridden by editor grid
52982     stopEditing : function(){},
52983
52984     /**
52985      * Returns the grid's SelectionModel.
52986      * @return {SelectionModel}
52987      */
52988     getSelectionModel : function(){
52989         if(!this.selModel){
52990             this.selModel = new Roo.grid.RowSelectionModel();
52991         }
52992         return this.selModel;
52993     },
52994
52995     /**
52996      * Returns the grid's DataSource.
52997      * @return {DataSource}
52998      */
52999     getDataSource : function(){
53000         return this.dataSource;
53001     },
53002
53003     /**
53004      * Returns the grid's ColumnModel.
53005      * @return {ColumnModel}
53006      */
53007     getColumnModel : function(){
53008         return this.colModel;
53009     },
53010
53011     /**
53012      * Returns the grid's GridView object.
53013      * @return {GridView}
53014      */
53015     getView : function(){
53016         if(!this.view){
53017             this.view = new Roo.grid.GridView(this.viewConfig);
53018         }
53019         return this.view;
53020     },
53021     /**
53022      * Called to get grid's drag proxy text, by default returns this.ddText.
53023      * @return {String}
53024      */
53025     getDragDropText : function(){
53026         var count = this.selModel.getCount();
53027         return String.format(this.ddText, count, count == 1 ? '' : 's');
53028     }
53029 });
53030 /**
53031  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53032  * %0 is replaced with the number of selected rows.
53033  * @type String
53034  */
53035 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53036  * Based on:
53037  * Ext JS Library 1.1.1
53038  * Copyright(c) 2006-2007, Ext JS, LLC.
53039  *
53040  * Originally Released Under LGPL - original licence link has changed is not relivant.
53041  *
53042  * Fork - LGPL
53043  * <script type="text/javascript">
53044  */
53045  
53046 Roo.grid.AbstractGridView = function(){
53047         this.grid = null;
53048         
53049         this.events = {
53050             "beforerowremoved" : true,
53051             "beforerowsinserted" : true,
53052             "beforerefresh" : true,
53053             "rowremoved" : true,
53054             "rowsinserted" : true,
53055             "rowupdated" : true,
53056             "refresh" : true
53057         };
53058     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53059 };
53060
53061 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53062     rowClass : "x-grid-row",
53063     cellClass : "x-grid-cell",
53064     tdClass : "x-grid-td",
53065     hdClass : "x-grid-hd",
53066     splitClass : "x-grid-hd-split",
53067     
53068     init: function(grid){
53069         this.grid = grid;
53070                 var cid = this.grid.getGridEl().id;
53071         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53072         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53073         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53074         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53075         },
53076         
53077     getColumnRenderers : function(){
53078         var renderers = [];
53079         var cm = this.grid.colModel;
53080         var colCount = cm.getColumnCount();
53081         for(var i = 0; i < colCount; i++){
53082             renderers[i] = cm.getRenderer(i);
53083         }
53084         return renderers;
53085     },
53086     
53087     getColumnIds : function(){
53088         var ids = [];
53089         var cm = this.grid.colModel;
53090         var colCount = cm.getColumnCount();
53091         for(var i = 0; i < colCount; i++){
53092             ids[i] = cm.getColumnId(i);
53093         }
53094         return ids;
53095     },
53096     
53097     getDataIndexes : function(){
53098         if(!this.indexMap){
53099             this.indexMap = this.buildIndexMap();
53100         }
53101         return this.indexMap.colToData;
53102     },
53103     
53104     getColumnIndexByDataIndex : function(dataIndex){
53105         if(!this.indexMap){
53106             this.indexMap = this.buildIndexMap();
53107         }
53108         return this.indexMap.dataToCol[dataIndex];
53109     },
53110     
53111     /**
53112      * Set a css style for a column dynamically. 
53113      * @param {Number} colIndex The index of the column
53114      * @param {String} name The css property name
53115      * @param {String} value The css value
53116      */
53117     setCSSStyle : function(colIndex, name, value){
53118         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53119         Roo.util.CSS.updateRule(selector, name, value);
53120     },
53121     
53122     generateRules : function(cm){
53123         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53124         Roo.util.CSS.removeStyleSheet(rulesId);
53125         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53126             var cid = cm.getColumnId(i);
53127             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53128                          this.tdSelector, cid, " {\n}\n",
53129                          this.hdSelector, cid, " {\n}\n",
53130                          this.splitSelector, cid, " {\n}\n");
53131         }
53132         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53133     }
53134 });/*
53135  * Based on:
53136  * Ext JS Library 1.1.1
53137  * Copyright(c) 2006-2007, Ext JS, LLC.
53138  *
53139  * Originally Released Under LGPL - original licence link has changed is not relivant.
53140  *
53141  * Fork - LGPL
53142  * <script type="text/javascript">
53143  */
53144
53145 // private
53146 // This is a support class used internally by the Grid components
53147 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53148     this.grid = grid;
53149     this.view = grid.getView();
53150     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53151     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53152     if(hd2){
53153         this.setHandleElId(Roo.id(hd));
53154         this.setOuterHandleElId(Roo.id(hd2));
53155     }
53156     this.scroll = false;
53157 };
53158 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53159     maxDragWidth: 120,
53160     getDragData : function(e){
53161         var t = Roo.lib.Event.getTarget(e);
53162         var h = this.view.findHeaderCell(t);
53163         if(h){
53164             return {ddel: h.firstChild, header:h};
53165         }
53166         return false;
53167     },
53168
53169     onInitDrag : function(e){
53170         this.view.headersDisabled = true;
53171         var clone = this.dragData.ddel.cloneNode(true);
53172         clone.id = Roo.id();
53173         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53174         this.proxy.update(clone);
53175         return true;
53176     },
53177
53178     afterValidDrop : function(){
53179         var v = this.view;
53180         setTimeout(function(){
53181             v.headersDisabled = false;
53182         }, 50);
53183     },
53184
53185     afterInvalidDrop : function(){
53186         var v = this.view;
53187         setTimeout(function(){
53188             v.headersDisabled = false;
53189         }, 50);
53190     }
53191 });
53192 /*
53193  * Based on:
53194  * Ext JS Library 1.1.1
53195  * Copyright(c) 2006-2007, Ext JS, LLC.
53196  *
53197  * Originally Released Under LGPL - original licence link has changed is not relivant.
53198  *
53199  * Fork - LGPL
53200  * <script type="text/javascript">
53201  */
53202 // private
53203 // This is a support class used internally by the Grid components
53204 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53205     this.grid = grid;
53206     this.view = grid.getView();
53207     // split the proxies so they don't interfere with mouse events
53208     this.proxyTop = Roo.DomHelper.append(document.body, {
53209         cls:"col-move-top", html:"&#160;"
53210     }, true);
53211     this.proxyBottom = Roo.DomHelper.append(document.body, {
53212         cls:"col-move-bottom", html:"&#160;"
53213     }, true);
53214     this.proxyTop.hide = this.proxyBottom.hide = function(){
53215         this.setLeftTop(-100,-100);
53216         this.setStyle("visibility", "hidden");
53217     };
53218     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53219     // temporarily disabled
53220     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53221     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53222 };
53223 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53224     proxyOffsets : [-4, -9],
53225     fly: Roo.Element.fly,
53226
53227     getTargetFromEvent : function(e){
53228         var t = Roo.lib.Event.getTarget(e);
53229         var cindex = this.view.findCellIndex(t);
53230         if(cindex !== false){
53231             return this.view.getHeaderCell(cindex);
53232         }
53233         return null;
53234     },
53235
53236     nextVisible : function(h){
53237         var v = this.view, cm = this.grid.colModel;
53238         h = h.nextSibling;
53239         while(h){
53240             if(!cm.isHidden(v.getCellIndex(h))){
53241                 return h;
53242             }
53243             h = h.nextSibling;
53244         }
53245         return null;
53246     },
53247
53248     prevVisible : function(h){
53249         var v = this.view, cm = this.grid.colModel;
53250         h = h.prevSibling;
53251         while(h){
53252             if(!cm.isHidden(v.getCellIndex(h))){
53253                 return h;
53254             }
53255             h = h.prevSibling;
53256         }
53257         return null;
53258     },
53259
53260     positionIndicator : function(h, n, e){
53261         var x = Roo.lib.Event.getPageX(e);
53262         var r = Roo.lib.Dom.getRegion(n.firstChild);
53263         var px, pt, py = r.top + this.proxyOffsets[1];
53264         if((r.right - x) <= (r.right-r.left)/2){
53265             px = r.right+this.view.borderWidth;
53266             pt = "after";
53267         }else{
53268             px = r.left;
53269             pt = "before";
53270         }
53271         var oldIndex = this.view.getCellIndex(h);
53272         var newIndex = this.view.getCellIndex(n);
53273
53274         if(this.grid.colModel.isFixed(newIndex)){
53275             return false;
53276         }
53277
53278         var locked = this.grid.colModel.isLocked(newIndex);
53279
53280         if(pt == "after"){
53281             newIndex++;
53282         }
53283         if(oldIndex < newIndex){
53284             newIndex--;
53285         }
53286         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53287             return false;
53288         }
53289         px +=  this.proxyOffsets[0];
53290         this.proxyTop.setLeftTop(px, py);
53291         this.proxyTop.show();
53292         if(!this.bottomOffset){
53293             this.bottomOffset = this.view.mainHd.getHeight();
53294         }
53295         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53296         this.proxyBottom.show();
53297         return pt;
53298     },
53299
53300     onNodeEnter : function(n, dd, e, data){
53301         if(data.header != n){
53302             this.positionIndicator(data.header, n, e);
53303         }
53304     },
53305
53306     onNodeOver : function(n, dd, e, data){
53307         var result = false;
53308         if(data.header != n){
53309             result = this.positionIndicator(data.header, n, e);
53310         }
53311         if(!result){
53312             this.proxyTop.hide();
53313             this.proxyBottom.hide();
53314         }
53315         return result ? this.dropAllowed : this.dropNotAllowed;
53316     },
53317
53318     onNodeOut : function(n, dd, e, data){
53319         this.proxyTop.hide();
53320         this.proxyBottom.hide();
53321     },
53322
53323     onNodeDrop : function(n, dd, e, data){
53324         var h = data.header;
53325         if(h != n){
53326             var cm = this.grid.colModel;
53327             var x = Roo.lib.Event.getPageX(e);
53328             var r = Roo.lib.Dom.getRegion(n.firstChild);
53329             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53330             var oldIndex = this.view.getCellIndex(h);
53331             var newIndex = this.view.getCellIndex(n);
53332             var locked = cm.isLocked(newIndex);
53333             if(pt == "after"){
53334                 newIndex++;
53335             }
53336             if(oldIndex < newIndex){
53337                 newIndex--;
53338             }
53339             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53340                 return false;
53341             }
53342             cm.setLocked(oldIndex, locked, true);
53343             cm.moveColumn(oldIndex, newIndex);
53344             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53345             return true;
53346         }
53347         return false;
53348     }
53349 });
53350 /*
53351  * Based on:
53352  * Ext JS Library 1.1.1
53353  * Copyright(c) 2006-2007, Ext JS, LLC.
53354  *
53355  * Originally Released Under LGPL - original licence link has changed is not relivant.
53356  *
53357  * Fork - LGPL
53358  * <script type="text/javascript">
53359  */
53360   
53361 /**
53362  * @class Roo.grid.GridView
53363  * @extends Roo.util.Observable
53364  *
53365  * @constructor
53366  * @param {Object} config
53367  */
53368 Roo.grid.GridView = function(config){
53369     Roo.grid.GridView.superclass.constructor.call(this);
53370     this.el = null;
53371
53372     Roo.apply(this, config);
53373 };
53374
53375 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53376
53377     unselectable :  'unselectable="on"',
53378     unselectableCls :  'x-unselectable',
53379     
53380     
53381     rowClass : "x-grid-row",
53382
53383     cellClass : "x-grid-col",
53384
53385     tdClass : "x-grid-td",
53386
53387     hdClass : "x-grid-hd",
53388
53389     splitClass : "x-grid-split",
53390
53391     sortClasses : ["sort-asc", "sort-desc"],
53392
53393     enableMoveAnim : false,
53394
53395     hlColor: "C3DAF9",
53396
53397     dh : Roo.DomHelper,
53398
53399     fly : Roo.Element.fly,
53400
53401     css : Roo.util.CSS,
53402
53403     borderWidth: 1,
53404
53405     splitOffset: 3,
53406
53407     scrollIncrement : 22,
53408
53409     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53410
53411     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53412
53413     bind : function(ds, cm){
53414         if(this.ds){
53415             this.ds.un("load", this.onLoad, this);
53416             this.ds.un("datachanged", this.onDataChange, this);
53417             this.ds.un("add", this.onAdd, this);
53418             this.ds.un("remove", this.onRemove, this);
53419             this.ds.un("update", this.onUpdate, this);
53420             this.ds.un("clear", this.onClear, this);
53421         }
53422         if(ds){
53423             ds.on("load", this.onLoad, this);
53424             ds.on("datachanged", this.onDataChange, this);
53425             ds.on("add", this.onAdd, this);
53426             ds.on("remove", this.onRemove, this);
53427             ds.on("update", this.onUpdate, this);
53428             ds.on("clear", this.onClear, this);
53429         }
53430         this.ds = ds;
53431
53432         if(this.cm){
53433             this.cm.un("widthchange", this.onColWidthChange, this);
53434             this.cm.un("headerchange", this.onHeaderChange, this);
53435             this.cm.un("hiddenchange", this.onHiddenChange, this);
53436             this.cm.un("columnmoved", this.onColumnMove, this);
53437             this.cm.un("columnlockchange", this.onColumnLock, this);
53438         }
53439         if(cm){
53440             this.generateRules(cm);
53441             cm.on("widthchange", this.onColWidthChange, this);
53442             cm.on("headerchange", this.onHeaderChange, this);
53443             cm.on("hiddenchange", this.onHiddenChange, this);
53444             cm.on("columnmoved", this.onColumnMove, this);
53445             cm.on("columnlockchange", this.onColumnLock, this);
53446         }
53447         this.cm = cm;
53448     },
53449
53450     init: function(grid){
53451         Roo.grid.GridView.superclass.init.call(this, grid);
53452
53453         this.bind(grid.dataSource, grid.colModel);
53454
53455         grid.on("headerclick", this.handleHeaderClick, this);
53456
53457         if(grid.trackMouseOver){
53458             grid.on("mouseover", this.onRowOver, this);
53459             grid.on("mouseout", this.onRowOut, this);
53460         }
53461         grid.cancelTextSelection = function(){};
53462         this.gridId = grid.id;
53463
53464         var tpls = this.templates || {};
53465
53466         if(!tpls.master){
53467             tpls.master = new Roo.Template(
53468                '<div class="x-grid" hidefocus="true">',
53469                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53470                   '<div class="x-grid-topbar"></div>',
53471                   '<div class="x-grid-scroller"><div></div></div>',
53472                   '<div class="x-grid-locked">',
53473                       '<div class="x-grid-header">{lockedHeader}</div>',
53474                       '<div class="x-grid-body">{lockedBody}</div>',
53475                   "</div>",
53476                   '<div class="x-grid-viewport">',
53477                       '<div class="x-grid-header">{header}</div>',
53478                       '<div class="x-grid-body">{body}</div>',
53479                   "</div>",
53480                   '<div class="x-grid-bottombar"></div>',
53481                  
53482                   '<div class="x-grid-resize-proxy">&#160;</div>',
53483                "</div>"
53484             );
53485             tpls.master.disableformats = true;
53486         }
53487
53488         if(!tpls.header){
53489             tpls.header = new Roo.Template(
53490                '<table border="0" cellspacing="0" cellpadding="0">',
53491                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53492                "</table>{splits}"
53493             );
53494             tpls.header.disableformats = true;
53495         }
53496         tpls.header.compile();
53497
53498         if(!tpls.hcell){
53499             tpls.hcell = new Roo.Template(
53500                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53501                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53502                 "</div></td>"
53503              );
53504              tpls.hcell.disableFormats = true;
53505         }
53506         tpls.hcell.compile();
53507
53508         if(!tpls.hsplit){
53509             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53510                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53511             tpls.hsplit.disableFormats = true;
53512         }
53513         tpls.hsplit.compile();
53514
53515         if(!tpls.body){
53516             tpls.body = new Roo.Template(
53517                '<table border="0" cellspacing="0" cellpadding="0">',
53518                "<tbody>{rows}</tbody>",
53519                "</table>"
53520             );
53521             tpls.body.disableFormats = true;
53522         }
53523         tpls.body.compile();
53524
53525         if(!tpls.row){
53526             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53527             tpls.row.disableFormats = true;
53528         }
53529         tpls.row.compile();
53530
53531         if(!tpls.cell){
53532             tpls.cell = new Roo.Template(
53533                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53534                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53535                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53536                 "</td>"
53537             );
53538             tpls.cell.disableFormats = true;
53539         }
53540         tpls.cell.compile();
53541
53542         this.templates = tpls;
53543     },
53544
53545     // remap these for backwards compat
53546     onColWidthChange : function(){
53547         this.updateColumns.apply(this, arguments);
53548     },
53549     onHeaderChange : function(){
53550         this.updateHeaders.apply(this, arguments);
53551     }, 
53552     onHiddenChange : function(){
53553         this.handleHiddenChange.apply(this, arguments);
53554     },
53555     onColumnMove : function(){
53556         this.handleColumnMove.apply(this, arguments);
53557     },
53558     onColumnLock : function(){
53559         this.handleLockChange.apply(this, arguments);
53560     },
53561
53562     onDataChange : function(){
53563         this.refresh();
53564         this.updateHeaderSortState();
53565     },
53566
53567     onClear : function(){
53568         this.refresh();
53569     },
53570
53571     onUpdate : function(ds, record){
53572         this.refreshRow(record);
53573     },
53574
53575     refreshRow : function(record){
53576         var ds = this.ds, index;
53577         if(typeof record == 'number'){
53578             index = record;
53579             record = ds.getAt(index);
53580         }else{
53581             index = ds.indexOf(record);
53582         }
53583         this.insertRows(ds, index, index, true);
53584         this.onRemove(ds, record, index+1, true);
53585         this.syncRowHeights(index, index);
53586         this.layout();
53587         this.fireEvent("rowupdated", this, index, record);
53588     },
53589
53590     onAdd : function(ds, records, index){
53591         this.insertRows(ds, index, index + (records.length-1));
53592     },
53593
53594     onRemove : function(ds, record, index, isUpdate){
53595         if(isUpdate !== true){
53596             this.fireEvent("beforerowremoved", this, index, record);
53597         }
53598         var bt = this.getBodyTable(), lt = this.getLockedTable();
53599         if(bt.rows[index]){
53600             bt.firstChild.removeChild(bt.rows[index]);
53601         }
53602         if(lt.rows[index]){
53603             lt.firstChild.removeChild(lt.rows[index]);
53604         }
53605         if(isUpdate !== true){
53606             this.stripeRows(index);
53607             this.syncRowHeights(index, index);
53608             this.layout();
53609             this.fireEvent("rowremoved", this, index, record);
53610         }
53611     },
53612
53613     onLoad : function(){
53614         this.scrollToTop();
53615     },
53616
53617     /**
53618      * Scrolls the grid to the top
53619      */
53620     scrollToTop : function(){
53621         if(this.scroller){
53622             this.scroller.dom.scrollTop = 0;
53623             this.syncScroll();
53624         }
53625     },
53626
53627     /**
53628      * Gets a panel in the header of the grid that can be used for toolbars etc.
53629      * After modifying the contents of this panel a call to grid.autoSize() may be
53630      * required to register any changes in size.
53631      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53632      * @return Roo.Element
53633      */
53634     getHeaderPanel : function(doShow){
53635         if(doShow){
53636             this.headerPanel.show();
53637         }
53638         return this.headerPanel;
53639     },
53640
53641     /**
53642      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53643      * After modifying the contents of this panel a call to grid.autoSize() may be
53644      * required to register any changes in size.
53645      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53646      * @return Roo.Element
53647      */
53648     getFooterPanel : function(doShow){
53649         if(doShow){
53650             this.footerPanel.show();
53651         }
53652         return this.footerPanel;
53653     },
53654
53655     initElements : function(){
53656         var E = Roo.Element;
53657         var el = this.grid.getGridEl().dom.firstChild;
53658         var cs = el.childNodes;
53659
53660         this.el = new E(el);
53661         
53662          this.focusEl = new E(el.firstChild);
53663         this.focusEl.swallowEvent("click", true);
53664         
53665         this.headerPanel = new E(cs[1]);
53666         this.headerPanel.enableDisplayMode("block");
53667
53668         this.scroller = new E(cs[2]);
53669         this.scrollSizer = new E(this.scroller.dom.firstChild);
53670
53671         this.lockedWrap = new E(cs[3]);
53672         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53673         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53674
53675         this.mainWrap = new E(cs[4]);
53676         this.mainHd = new E(this.mainWrap.dom.firstChild);
53677         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53678
53679         this.footerPanel = new E(cs[5]);
53680         this.footerPanel.enableDisplayMode("block");
53681
53682         this.resizeProxy = new E(cs[6]);
53683
53684         this.headerSelector = String.format(
53685            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53686            this.lockedHd.id, this.mainHd.id
53687         );
53688
53689         this.splitterSelector = String.format(
53690            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53691            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53692         );
53693     },
53694     idToCssName : function(s)
53695     {
53696         return s.replace(/[^a-z0-9]+/ig, '-');
53697     },
53698
53699     getHeaderCell : function(index){
53700         return Roo.DomQuery.select(this.headerSelector)[index];
53701     },
53702
53703     getHeaderCellMeasure : function(index){
53704         return this.getHeaderCell(index).firstChild;
53705     },
53706
53707     getHeaderCellText : function(index){
53708         return this.getHeaderCell(index).firstChild.firstChild;
53709     },
53710
53711     getLockedTable : function(){
53712         return this.lockedBody.dom.firstChild;
53713     },
53714
53715     getBodyTable : function(){
53716         return this.mainBody.dom.firstChild;
53717     },
53718
53719     getLockedRow : function(index){
53720         return this.getLockedTable().rows[index];
53721     },
53722
53723     getRow : function(index){
53724         return this.getBodyTable().rows[index];
53725     },
53726
53727     getRowComposite : function(index){
53728         if(!this.rowEl){
53729             this.rowEl = new Roo.CompositeElementLite();
53730         }
53731         var els = [], lrow, mrow;
53732         if(lrow = this.getLockedRow(index)){
53733             els.push(lrow);
53734         }
53735         if(mrow = this.getRow(index)){
53736             els.push(mrow);
53737         }
53738         this.rowEl.elements = els;
53739         return this.rowEl;
53740     },
53741     /**
53742      * Gets the 'td' of the cell
53743      * 
53744      * @param {Integer} rowIndex row to select
53745      * @param {Integer} colIndex column to select
53746      * 
53747      * @return {Object} 
53748      */
53749     getCell : function(rowIndex, colIndex){
53750         var locked = this.cm.getLockedCount();
53751         var source;
53752         if(colIndex < locked){
53753             source = this.lockedBody.dom.firstChild;
53754         }else{
53755             source = this.mainBody.dom.firstChild;
53756             colIndex -= locked;
53757         }
53758         return source.rows[rowIndex].childNodes[colIndex];
53759     },
53760
53761     getCellText : function(rowIndex, colIndex){
53762         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53763     },
53764
53765     getCellBox : function(cell){
53766         var b = this.fly(cell).getBox();
53767         if(Roo.isOpera){ // opera fails to report the Y
53768             b.y = cell.offsetTop + this.mainBody.getY();
53769         }
53770         return b;
53771     },
53772
53773     getCellIndex : function(cell){
53774         var id = String(cell.className).match(this.cellRE);
53775         if(id){
53776             return parseInt(id[1], 10);
53777         }
53778         return 0;
53779     },
53780
53781     findHeaderIndex : function(n){
53782         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53783         return r ? this.getCellIndex(r) : false;
53784     },
53785
53786     findHeaderCell : function(n){
53787         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53788         return r ? r : false;
53789     },
53790
53791     findRowIndex : function(n){
53792         if(!n){
53793             return false;
53794         }
53795         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53796         return r ? r.rowIndex : false;
53797     },
53798
53799     findCellIndex : function(node){
53800         var stop = this.el.dom;
53801         while(node && node != stop){
53802             if(this.findRE.test(node.className)){
53803                 return this.getCellIndex(node);
53804             }
53805             node = node.parentNode;
53806         }
53807         return false;
53808     },
53809
53810     getColumnId : function(index){
53811         return this.cm.getColumnId(index);
53812     },
53813
53814     getSplitters : function()
53815     {
53816         if(this.splitterSelector){
53817            return Roo.DomQuery.select(this.splitterSelector);
53818         }else{
53819             return null;
53820       }
53821     },
53822
53823     getSplitter : function(index){
53824         return this.getSplitters()[index];
53825     },
53826
53827     onRowOver : function(e, t){
53828         var row;
53829         if((row = this.findRowIndex(t)) !== false){
53830             this.getRowComposite(row).addClass("x-grid-row-over");
53831         }
53832     },
53833
53834     onRowOut : function(e, t){
53835         var row;
53836         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53837             this.getRowComposite(row).removeClass("x-grid-row-over");
53838         }
53839     },
53840
53841     renderHeaders : function(){
53842         var cm = this.cm;
53843         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53844         var cb = [], lb = [], sb = [], lsb = [], p = {};
53845         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53846             p.cellId = "x-grid-hd-0-" + i;
53847             p.splitId = "x-grid-csplit-0-" + i;
53848             p.id = cm.getColumnId(i);
53849             p.title = cm.getColumnTooltip(i) || "";
53850             p.value = cm.getColumnHeader(i) || "";
53851             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53852             if(!cm.isLocked(i)){
53853                 cb[cb.length] = ct.apply(p);
53854                 sb[sb.length] = st.apply(p);
53855             }else{
53856                 lb[lb.length] = ct.apply(p);
53857                 lsb[lsb.length] = st.apply(p);
53858             }
53859         }
53860         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53861                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53862     },
53863
53864     updateHeaders : function(){
53865         var html = this.renderHeaders();
53866         this.lockedHd.update(html[0]);
53867         this.mainHd.update(html[1]);
53868     },
53869
53870     /**
53871      * Focuses the specified row.
53872      * @param {Number} row The row index
53873      */
53874     focusRow : function(row)
53875     {
53876         //Roo.log('GridView.focusRow');
53877         var x = this.scroller.dom.scrollLeft;
53878         this.focusCell(row, 0, false);
53879         this.scroller.dom.scrollLeft = x;
53880     },
53881
53882     /**
53883      * Focuses the specified cell.
53884      * @param {Number} row The row index
53885      * @param {Number} col The column index
53886      * @param {Boolean} hscroll false to disable horizontal scrolling
53887      */
53888     focusCell : function(row, col, hscroll)
53889     {
53890         //Roo.log('GridView.focusCell');
53891         var el = this.ensureVisible(row, col, hscroll);
53892         this.focusEl.alignTo(el, "tl-tl");
53893         if(Roo.isGecko){
53894             this.focusEl.focus();
53895         }else{
53896             this.focusEl.focus.defer(1, this.focusEl);
53897         }
53898     },
53899
53900     /**
53901      * Scrolls the specified cell into view
53902      * @param {Number} row The row index
53903      * @param {Number} col The column index
53904      * @param {Boolean} hscroll false to disable horizontal scrolling
53905      */
53906     ensureVisible : function(row, col, hscroll)
53907     {
53908         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53909         //return null; //disable for testing.
53910         if(typeof row != "number"){
53911             row = row.rowIndex;
53912         }
53913         if(row < 0 && row >= this.ds.getCount()){
53914             return  null;
53915         }
53916         col = (col !== undefined ? col : 0);
53917         var cm = this.grid.colModel;
53918         while(cm.isHidden(col)){
53919             col++;
53920         }
53921
53922         var el = this.getCell(row, col);
53923         if(!el){
53924             return null;
53925         }
53926         var c = this.scroller.dom;
53927
53928         var ctop = parseInt(el.offsetTop, 10);
53929         var cleft = parseInt(el.offsetLeft, 10);
53930         var cbot = ctop + el.offsetHeight;
53931         var cright = cleft + el.offsetWidth;
53932         
53933         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53934         var stop = parseInt(c.scrollTop, 10);
53935         var sleft = parseInt(c.scrollLeft, 10);
53936         var sbot = stop + ch;
53937         var sright = sleft + c.clientWidth;
53938         /*
53939         Roo.log('GridView.ensureVisible:' +
53940                 ' ctop:' + ctop +
53941                 ' c.clientHeight:' + c.clientHeight +
53942                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53943                 ' stop:' + stop +
53944                 ' cbot:' + cbot +
53945                 ' sbot:' + sbot +
53946                 ' ch:' + ch  
53947                 );
53948         */
53949         if(ctop < stop){
53950              c.scrollTop = ctop;
53951             //Roo.log("set scrolltop to ctop DISABLE?");
53952         }else if(cbot > sbot){
53953             //Roo.log("set scrolltop to cbot-ch");
53954             c.scrollTop = cbot-ch;
53955         }
53956         
53957         if(hscroll !== false){
53958             if(cleft < sleft){
53959                 c.scrollLeft = cleft;
53960             }else if(cright > sright){
53961                 c.scrollLeft = cright-c.clientWidth;
53962             }
53963         }
53964          
53965         return el;
53966     },
53967
53968     updateColumns : function(){
53969         this.grid.stopEditing();
53970         var cm = this.grid.colModel, colIds = this.getColumnIds();
53971         //var totalWidth = cm.getTotalWidth();
53972         var pos = 0;
53973         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53974             //if(cm.isHidden(i)) continue;
53975             var w = cm.getColumnWidth(i);
53976             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53977             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53978         }
53979         this.updateSplitters();
53980     },
53981
53982     generateRules : function(cm){
53983         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53984         Roo.util.CSS.removeStyleSheet(rulesId);
53985         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53986             var cid = cm.getColumnId(i);
53987             var align = '';
53988             if(cm.config[i].align){
53989                 align = 'text-align:'+cm.config[i].align+';';
53990             }
53991             var hidden = '';
53992             if(cm.isHidden(i)){
53993                 hidden = 'display:none;';
53994             }
53995             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53996             ruleBuf.push(
53997                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53998                     this.hdSelector, cid, " {\n", align, width, "}\n",
53999                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
54000                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
54001         }
54002         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54003     },
54004
54005     updateSplitters : function(){
54006         var cm = this.cm, s = this.getSplitters();
54007         if(s){ // splitters not created yet
54008             var pos = 0, locked = true;
54009             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54010                 if(cm.isHidden(i)) continue;
54011                 var w = cm.getColumnWidth(i); // make sure it's a number
54012                 if(!cm.isLocked(i) && locked){
54013                     pos = 0;
54014                     locked = false;
54015                 }
54016                 pos += w;
54017                 s[i].style.left = (pos-this.splitOffset) + "px";
54018             }
54019         }
54020     },
54021
54022     handleHiddenChange : function(colModel, colIndex, hidden){
54023         if(hidden){
54024             this.hideColumn(colIndex);
54025         }else{
54026             this.unhideColumn(colIndex);
54027         }
54028     },
54029
54030     hideColumn : function(colIndex){
54031         var cid = this.getColumnId(colIndex);
54032         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54033         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54034         if(Roo.isSafari){
54035             this.updateHeaders();
54036         }
54037         this.updateSplitters();
54038         this.layout();
54039     },
54040
54041     unhideColumn : function(colIndex){
54042         var cid = this.getColumnId(colIndex);
54043         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54044         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54045
54046         if(Roo.isSafari){
54047             this.updateHeaders();
54048         }
54049         this.updateSplitters();
54050         this.layout();
54051     },
54052
54053     insertRows : function(dm, firstRow, lastRow, isUpdate){
54054         if(firstRow == 0 && lastRow == dm.getCount()-1){
54055             this.refresh();
54056         }else{
54057             if(!isUpdate){
54058                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54059             }
54060             var s = this.getScrollState();
54061             var markup = this.renderRows(firstRow, lastRow);
54062             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54063             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54064             this.restoreScroll(s);
54065             if(!isUpdate){
54066                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54067                 this.syncRowHeights(firstRow, lastRow);
54068                 this.stripeRows(firstRow);
54069                 this.layout();
54070             }
54071         }
54072     },
54073
54074     bufferRows : function(markup, target, index){
54075         var before = null, trows = target.rows, tbody = target.tBodies[0];
54076         if(index < trows.length){
54077             before = trows[index];
54078         }
54079         var b = document.createElement("div");
54080         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54081         var rows = b.firstChild.rows;
54082         for(var i = 0, len = rows.length; i < len; i++){
54083             if(before){
54084                 tbody.insertBefore(rows[0], before);
54085             }else{
54086                 tbody.appendChild(rows[0]);
54087             }
54088         }
54089         b.innerHTML = "";
54090         b = null;
54091     },
54092
54093     deleteRows : function(dm, firstRow, lastRow){
54094         if(dm.getRowCount()<1){
54095             this.fireEvent("beforerefresh", this);
54096             this.mainBody.update("");
54097             this.lockedBody.update("");
54098             this.fireEvent("refresh", this);
54099         }else{
54100             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54101             var bt = this.getBodyTable();
54102             var tbody = bt.firstChild;
54103             var rows = bt.rows;
54104             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54105                 tbody.removeChild(rows[firstRow]);
54106             }
54107             this.stripeRows(firstRow);
54108             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54109         }
54110     },
54111
54112     updateRows : function(dataSource, firstRow, lastRow){
54113         var s = this.getScrollState();
54114         this.refresh();
54115         this.restoreScroll(s);
54116     },
54117
54118     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54119         if(!noRefresh){
54120            this.refresh();
54121         }
54122         this.updateHeaderSortState();
54123     },
54124
54125     getScrollState : function(){
54126         
54127         var sb = this.scroller.dom;
54128         return {left: sb.scrollLeft, top: sb.scrollTop};
54129     },
54130
54131     stripeRows : function(startRow){
54132         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54133             return;
54134         }
54135         startRow = startRow || 0;
54136         var rows = this.getBodyTable().rows;
54137         var lrows = this.getLockedTable().rows;
54138         var cls = ' x-grid-row-alt ';
54139         for(var i = startRow, len = rows.length; i < len; i++){
54140             var row = rows[i], lrow = lrows[i];
54141             var isAlt = ((i+1) % 2 == 0);
54142             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54143             if(isAlt == hasAlt){
54144                 continue;
54145             }
54146             if(isAlt){
54147                 row.className += " x-grid-row-alt";
54148             }else{
54149                 row.className = row.className.replace("x-grid-row-alt", "");
54150             }
54151             if(lrow){
54152                 lrow.className = row.className;
54153             }
54154         }
54155     },
54156
54157     restoreScroll : function(state){
54158         //Roo.log('GridView.restoreScroll');
54159         var sb = this.scroller.dom;
54160         sb.scrollLeft = state.left;
54161         sb.scrollTop = state.top;
54162         this.syncScroll();
54163     },
54164
54165     syncScroll : function(){
54166         //Roo.log('GridView.syncScroll');
54167         var sb = this.scroller.dom;
54168         var sh = this.mainHd.dom;
54169         var bs = this.mainBody.dom;
54170         var lv = this.lockedBody.dom;
54171         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54172         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54173     },
54174
54175     handleScroll : function(e){
54176         this.syncScroll();
54177         var sb = this.scroller.dom;
54178         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54179         e.stopEvent();
54180     },
54181
54182     handleWheel : function(e){
54183         var d = e.getWheelDelta();
54184         this.scroller.dom.scrollTop -= d*22;
54185         // set this here to prevent jumpy scrolling on large tables
54186         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54187         e.stopEvent();
54188     },
54189
54190     renderRows : function(startRow, endRow){
54191         // pull in all the crap needed to render rows
54192         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54193         var colCount = cm.getColumnCount();
54194
54195         if(ds.getCount() < 1){
54196             return ["", ""];
54197         }
54198
54199         // build a map for all the columns
54200         var cs = [];
54201         for(var i = 0; i < colCount; i++){
54202             var name = cm.getDataIndex(i);
54203             cs[i] = {
54204                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54205                 renderer : cm.getRenderer(i),
54206                 id : cm.getColumnId(i),
54207                 locked : cm.isLocked(i)
54208             };
54209         }
54210
54211         startRow = startRow || 0;
54212         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54213
54214         // records to render
54215         var rs = ds.getRange(startRow, endRow);
54216
54217         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54218     },
54219
54220     // As much as I hate to duplicate code, this was branched because FireFox really hates
54221     // [].join("") on strings. The performance difference was substantial enough to
54222     // branch this function
54223     doRender : Roo.isGecko ?
54224             function(cs, rs, ds, startRow, colCount, stripe){
54225                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54226                 // buffers
54227                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54228                 
54229                 var hasListener = this.grid.hasListener('rowclass');
54230                 var rowcfg = {};
54231                 for(var j = 0, len = rs.length; j < len; j++){
54232                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54233                     for(var i = 0; i < colCount; i++){
54234                         c = cs[i];
54235                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54236                         p.id = c.id;
54237                         p.css = p.attr = "";
54238                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54239                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54240                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54241                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54242                         }
54243                         var markup = ct.apply(p);
54244                         if(!c.locked){
54245                             cb+= markup;
54246                         }else{
54247                             lcb+= markup;
54248                         }
54249                     }
54250                     var alt = [];
54251                     if(stripe && ((rowIndex+1) % 2 == 0)){
54252                         alt.push("x-grid-row-alt")
54253                     }
54254                     if(r.dirty){
54255                         alt.push(  " x-grid-dirty-row");
54256                     }
54257                     rp.cells = lcb;
54258                     if(this.getRowClass){
54259                         alt.push(this.getRowClass(r, rowIndex));
54260                     }
54261                     if (hasListener) {
54262                         rowcfg = {
54263                              
54264                             record: r,
54265                             rowIndex : rowIndex,
54266                             rowClass : ''
54267                         }
54268                         this.grid.fireEvent('rowclass', this, rowcfg);
54269                         alt.push(rowcfg.rowClass);
54270                     }
54271                     rp.alt = alt.join(" ");
54272                     lbuf+= rt.apply(rp);
54273                     rp.cells = cb;
54274                     buf+=  rt.apply(rp);
54275                 }
54276                 return [lbuf, buf];
54277             } :
54278             function(cs, rs, ds, startRow, colCount, stripe){
54279                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54280                 // buffers
54281                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54282                 var hasListener = this.grid.hasListener('rowclass');
54283  
54284                 var rowcfg = {};
54285                 for(var j = 0, len = rs.length; j < len; j++){
54286                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54287                     for(var i = 0; i < colCount; i++){
54288                         c = cs[i];
54289                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54290                         p.id = c.id;
54291                         p.css = p.attr = "";
54292                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54293                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54294                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54295                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54296                         }
54297                         
54298                         var markup = ct.apply(p);
54299                         if(!c.locked){
54300                             cb[cb.length] = markup;
54301                         }else{
54302                             lcb[lcb.length] = markup;
54303                         }
54304                     }
54305                     var alt = [];
54306                     if(stripe && ((rowIndex+1) % 2 == 0)){
54307                         alt.push( "x-grid-row-alt");
54308                     }
54309                     if(r.dirty){
54310                         alt.push(" x-grid-dirty-row");
54311                     }
54312                     rp.cells = lcb;
54313                     if(this.getRowClass){
54314                         alt.push( this.getRowClass(r, rowIndex));
54315                     }
54316                     if (hasListener) {
54317                         rowcfg = {
54318                              
54319                             record: r,
54320                             rowIndex : rowIndex,
54321                             rowClass : ''
54322                         }
54323                         this.grid.fireEvent('rowclass', this, rowcfg);
54324                         alt.push(rowcfg.rowClass);
54325                     }
54326                     rp.alt = alt.join(" ");
54327                     rp.cells = lcb.join("");
54328                     lbuf[lbuf.length] = rt.apply(rp);
54329                     rp.cells = cb.join("");
54330                     buf[buf.length] =  rt.apply(rp);
54331                 }
54332                 return [lbuf.join(""), buf.join("")];
54333             },
54334
54335     renderBody : function(){
54336         var markup = this.renderRows();
54337         var bt = this.templates.body;
54338         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54339     },
54340
54341     /**
54342      * Refreshes the grid
54343      * @param {Boolean} headersToo
54344      */
54345     refresh : function(headersToo){
54346         this.fireEvent("beforerefresh", this);
54347         this.grid.stopEditing();
54348         var result = this.renderBody();
54349         this.lockedBody.update(result[0]);
54350         this.mainBody.update(result[1]);
54351         if(headersToo === true){
54352             this.updateHeaders();
54353             this.updateColumns();
54354             this.updateSplitters();
54355             this.updateHeaderSortState();
54356         }
54357         this.syncRowHeights();
54358         this.layout();
54359         this.fireEvent("refresh", this);
54360     },
54361
54362     handleColumnMove : function(cm, oldIndex, newIndex){
54363         this.indexMap = null;
54364         var s = this.getScrollState();
54365         this.refresh(true);
54366         this.restoreScroll(s);
54367         this.afterMove(newIndex);
54368     },
54369
54370     afterMove : function(colIndex){
54371         if(this.enableMoveAnim && Roo.enableFx){
54372             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54373         }
54374         // if multisort - fix sortOrder, and reload..
54375         if (this.grid.dataSource.multiSort) {
54376             // the we can call sort again..
54377             var dm = this.grid.dataSource;
54378             var cm = this.grid.colModel;
54379             var so = [];
54380             for(var i = 0; i < cm.config.length; i++ ) {
54381                 
54382                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54383                     continue; // dont' bother, it's not in sort list or being set.
54384                 }
54385                 
54386                 so.push(cm.config[i].dataIndex);
54387             };
54388             dm.sortOrder = so;
54389             dm.load(dm.lastOptions);
54390             
54391             
54392         }
54393         
54394     },
54395
54396     updateCell : function(dm, rowIndex, dataIndex){
54397         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54398         if(typeof colIndex == "undefined"){ // not present in grid
54399             return;
54400         }
54401         var cm = this.grid.colModel;
54402         var cell = this.getCell(rowIndex, colIndex);
54403         var cellText = this.getCellText(rowIndex, colIndex);
54404
54405         var p = {
54406             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54407             id : cm.getColumnId(colIndex),
54408             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54409         };
54410         var renderer = cm.getRenderer(colIndex);
54411         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54412         if(typeof val == "undefined" || val === "") val = "&#160;";
54413         cellText.innerHTML = val;
54414         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54415         this.syncRowHeights(rowIndex, rowIndex);
54416     },
54417
54418     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54419         var maxWidth = 0;
54420         if(this.grid.autoSizeHeaders){
54421             var h = this.getHeaderCellMeasure(colIndex);
54422             maxWidth = Math.max(maxWidth, h.scrollWidth);
54423         }
54424         var tb, index;
54425         if(this.cm.isLocked(colIndex)){
54426             tb = this.getLockedTable();
54427             index = colIndex;
54428         }else{
54429             tb = this.getBodyTable();
54430             index = colIndex - this.cm.getLockedCount();
54431         }
54432         if(tb && tb.rows){
54433             var rows = tb.rows;
54434             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54435             for(var i = 0; i < stopIndex; i++){
54436                 var cell = rows[i].childNodes[index].firstChild;
54437                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54438             }
54439         }
54440         return maxWidth + /*margin for error in IE*/ 5;
54441     },
54442     /**
54443      * Autofit a column to its content.
54444      * @param {Number} colIndex
54445      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54446      */
54447      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54448          if(this.cm.isHidden(colIndex)){
54449              return; // can't calc a hidden column
54450          }
54451         if(forceMinSize){
54452             var cid = this.cm.getColumnId(colIndex);
54453             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54454            if(this.grid.autoSizeHeaders){
54455                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54456            }
54457         }
54458         var newWidth = this.calcColumnWidth(colIndex);
54459         this.cm.setColumnWidth(colIndex,
54460             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54461         if(!suppressEvent){
54462             this.grid.fireEvent("columnresize", colIndex, newWidth);
54463         }
54464     },
54465
54466     /**
54467      * Autofits all columns to their content and then expands to fit any extra space in the grid
54468      */
54469      autoSizeColumns : function(){
54470         var cm = this.grid.colModel;
54471         var colCount = cm.getColumnCount();
54472         for(var i = 0; i < colCount; i++){
54473             this.autoSizeColumn(i, true, true);
54474         }
54475         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54476             this.fitColumns();
54477         }else{
54478             this.updateColumns();
54479             this.layout();
54480         }
54481     },
54482
54483     /**
54484      * Autofits all columns to the grid's width proportionate with their current size
54485      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54486      */
54487     fitColumns : function(reserveScrollSpace){
54488         var cm = this.grid.colModel;
54489         var colCount = cm.getColumnCount();
54490         var cols = [];
54491         var width = 0;
54492         var i, w;
54493         for (i = 0; i < colCount; i++){
54494             if(!cm.isHidden(i) && !cm.isFixed(i)){
54495                 w = cm.getColumnWidth(i);
54496                 cols.push(i);
54497                 cols.push(w);
54498                 width += w;
54499             }
54500         }
54501         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54502         if(reserveScrollSpace){
54503             avail -= 17;
54504         }
54505         var frac = (avail - cm.getTotalWidth())/width;
54506         while (cols.length){
54507             w = cols.pop();
54508             i = cols.pop();
54509             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54510         }
54511         this.updateColumns();
54512         this.layout();
54513     },
54514
54515     onRowSelect : function(rowIndex){
54516         var row = this.getRowComposite(rowIndex);
54517         row.addClass("x-grid-row-selected");
54518     },
54519
54520     onRowDeselect : function(rowIndex){
54521         var row = this.getRowComposite(rowIndex);
54522         row.removeClass("x-grid-row-selected");
54523     },
54524
54525     onCellSelect : function(row, col){
54526         var cell = this.getCell(row, col);
54527         if(cell){
54528             Roo.fly(cell).addClass("x-grid-cell-selected");
54529         }
54530     },
54531
54532     onCellDeselect : function(row, col){
54533         var cell = this.getCell(row, col);
54534         if(cell){
54535             Roo.fly(cell).removeClass("x-grid-cell-selected");
54536         }
54537     },
54538
54539     updateHeaderSortState : function(){
54540         
54541         // sort state can be single { field: xxx, direction : yyy}
54542         // or   { xxx=>ASC , yyy : DESC ..... }
54543         
54544         var mstate = {};
54545         if (!this.ds.multiSort) { 
54546             var state = this.ds.getSortState();
54547             if(!state){
54548                 return;
54549             }
54550             mstate[state.field] = state.direction;
54551             // FIXME... - this is not used here.. but might be elsewhere..
54552             this.sortState = state;
54553             
54554         } else {
54555             mstate = this.ds.sortToggle;
54556         }
54557         //remove existing sort classes..
54558         
54559         var sc = this.sortClasses;
54560         var hds = this.el.select(this.headerSelector).removeClass(sc);
54561         
54562         for(var f in mstate) {
54563         
54564             var sortColumn = this.cm.findColumnIndex(f);
54565             
54566             if(sortColumn != -1){
54567                 var sortDir = mstate[f];        
54568                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54569             }
54570         }
54571         
54572          
54573         
54574     },
54575
54576
54577     handleHeaderClick : function(g, index,e){
54578         
54579         Roo.log("header click");
54580         
54581         if (Roo.isTouch) {
54582             // touch events on header are handled by context
54583             this.handleHdCtx(g,index,e);
54584             return;
54585         }
54586         
54587         
54588         if(this.headersDisabled){
54589             return;
54590         }
54591         var dm = g.dataSource, cm = g.colModel;
54592         if(!cm.isSortable(index)){
54593             return;
54594         }
54595         g.stopEditing();
54596         
54597         if (dm.multiSort) {
54598             // update the sortOrder
54599             var so = [];
54600             for(var i = 0; i < cm.config.length; i++ ) {
54601                 
54602                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54603                     continue; // dont' bother, it's not in sort list or being set.
54604                 }
54605                 
54606                 so.push(cm.config[i].dataIndex);
54607             };
54608             dm.sortOrder = so;
54609         }
54610         
54611         
54612         dm.sort(cm.getDataIndex(index));
54613     },
54614
54615
54616     destroy : function(){
54617         if(this.colMenu){
54618             this.colMenu.removeAll();
54619             Roo.menu.MenuMgr.unregister(this.colMenu);
54620             this.colMenu.getEl().remove();
54621             delete this.colMenu;
54622         }
54623         if(this.hmenu){
54624             this.hmenu.removeAll();
54625             Roo.menu.MenuMgr.unregister(this.hmenu);
54626             this.hmenu.getEl().remove();
54627             delete this.hmenu;
54628         }
54629         if(this.grid.enableColumnMove){
54630             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54631             if(dds){
54632                 for(var dd in dds){
54633                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54634                         var elid = dds[dd].dragElId;
54635                         dds[dd].unreg();
54636                         Roo.get(elid).remove();
54637                     } else if(dds[dd].config.isTarget){
54638                         dds[dd].proxyTop.remove();
54639                         dds[dd].proxyBottom.remove();
54640                         dds[dd].unreg();
54641                     }
54642                     if(Roo.dd.DDM.locationCache[dd]){
54643                         delete Roo.dd.DDM.locationCache[dd];
54644                     }
54645                 }
54646                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54647             }
54648         }
54649         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54650         this.bind(null, null);
54651         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54652     },
54653
54654     handleLockChange : function(){
54655         this.refresh(true);
54656     },
54657
54658     onDenyColumnLock : function(){
54659
54660     },
54661
54662     onDenyColumnHide : function(){
54663
54664     },
54665
54666     handleHdMenuClick : function(item){
54667         var index = this.hdCtxIndex;
54668         var cm = this.cm, ds = this.ds;
54669         switch(item.id){
54670             case "asc":
54671                 ds.sort(cm.getDataIndex(index), "ASC");
54672                 break;
54673             case "desc":
54674                 ds.sort(cm.getDataIndex(index), "DESC");
54675                 break;
54676             case "lock":
54677                 var lc = cm.getLockedCount();
54678                 if(cm.getColumnCount(true) <= lc+1){
54679                     this.onDenyColumnLock();
54680                     return;
54681                 }
54682                 if(lc != index){
54683                     cm.setLocked(index, true, true);
54684                     cm.moveColumn(index, lc);
54685                     this.grid.fireEvent("columnmove", index, lc);
54686                 }else{
54687                     cm.setLocked(index, true);
54688                 }
54689             break;
54690             case "unlock":
54691                 var lc = cm.getLockedCount();
54692                 if((lc-1) != index){
54693                     cm.setLocked(index, false, true);
54694                     cm.moveColumn(index, lc-1);
54695                     this.grid.fireEvent("columnmove", index, lc-1);
54696                 }else{
54697                     cm.setLocked(index, false);
54698                 }
54699             break;
54700             case 'wider': // used to expand cols on touch..
54701             case 'narrow':
54702                 var cw = cm.getColumnWidth(index);
54703                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54704                 cw = Math.max(0, cw);
54705                 cw = Math.min(cw,4000);
54706                 cm.setColumnWidth(index, cw);
54707                 break;
54708                 
54709             default:
54710                 index = cm.getIndexById(item.id.substr(4));
54711                 if(index != -1){
54712                     if(item.checked && cm.getColumnCount(true) <= 1){
54713                         this.onDenyColumnHide();
54714                         return false;
54715                     }
54716                     cm.setHidden(index, item.checked);
54717                 }
54718         }
54719         return true;
54720     },
54721
54722     beforeColMenuShow : function(){
54723         var cm = this.cm,  colCount = cm.getColumnCount();
54724         this.colMenu.removeAll();
54725         for(var i = 0; i < colCount; i++){
54726             this.colMenu.add(new Roo.menu.CheckItem({
54727                 id: "col-"+cm.getColumnId(i),
54728                 text: cm.getColumnHeader(i),
54729                 checked: !cm.isHidden(i),
54730                 hideOnClick:false
54731             }));
54732         }
54733     },
54734
54735     handleHdCtx : function(g, index, e){
54736         e.stopEvent();
54737         var hd = this.getHeaderCell(index);
54738         this.hdCtxIndex = index;
54739         var ms = this.hmenu.items, cm = this.cm;
54740         ms.get("asc").setDisabled(!cm.isSortable(index));
54741         ms.get("desc").setDisabled(!cm.isSortable(index));
54742         if(this.grid.enableColLock !== false){
54743             ms.get("lock").setDisabled(cm.isLocked(index));
54744             ms.get("unlock").setDisabled(!cm.isLocked(index));
54745         }
54746         this.hmenu.show(hd, "tl-bl");
54747     },
54748
54749     handleHdOver : function(e){
54750         var hd = this.findHeaderCell(e.getTarget());
54751         if(hd && !this.headersDisabled){
54752             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54753                this.fly(hd).addClass("x-grid-hd-over");
54754             }
54755         }
54756     },
54757
54758     handleHdOut : function(e){
54759         var hd = this.findHeaderCell(e.getTarget());
54760         if(hd){
54761             this.fly(hd).removeClass("x-grid-hd-over");
54762         }
54763     },
54764
54765     handleSplitDblClick : function(e, t){
54766         var i = this.getCellIndex(t);
54767         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54768             this.autoSizeColumn(i, true);
54769             this.layout();
54770         }
54771     },
54772
54773     render : function(){
54774
54775         var cm = this.cm;
54776         var colCount = cm.getColumnCount();
54777
54778         if(this.grid.monitorWindowResize === true){
54779             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54780         }
54781         var header = this.renderHeaders();
54782         var body = this.templates.body.apply({rows:""});
54783         var html = this.templates.master.apply({
54784             lockedBody: body,
54785             body: body,
54786             lockedHeader: header[0],
54787             header: header[1]
54788         });
54789
54790         //this.updateColumns();
54791
54792         this.grid.getGridEl().dom.innerHTML = html;
54793
54794         this.initElements();
54795         
54796         // a kludge to fix the random scolling effect in webkit
54797         this.el.on("scroll", function() {
54798             this.el.dom.scrollTop=0; // hopefully not recursive..
54799         },this);
54800
54801         this.scroller.on("scroll", this.handleScroll, this);
54802         this.lockedBody.on("mousewheel", this.handleWheel, this);
54803         this.mainBody.on("mousewheel", this.handleWheel, this);
54804
54805         this.mainHd.on("mouseover", this.handleHdOver, this);
54806         this.mainHd.on("mouseout", this.handleHdOut, this);
54807         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54808                 {delegate: "."+this.splitClass});
54809
54810         this.lockedHd.on("mouseover", this.handleHdOver, this);
54811         this.lockedHd.on("mouseout", this.handleHdOut, this);
54812         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54813                 {delegate: "."+this.splitClass});
54814
54815         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54816             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54817         }
54818
54819         this.updateSplitters();
54820
54821         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54822             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54823             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54824         }
54825
54826         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54827             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54828             this.hmenu.add(
54829                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54830                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54831             );
54832             if(this.grid.enableColLock !== false){
54833                 this.hmenu.add('-',
54834                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54835                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54836                 );
54837             }
54838             if (Roo.isTouch) {
54839                  this.hmenu.add('-',
54840                     {id:"wider", text: this.columnsWiderText},
54841                     {id:"narrow", text: this.columnsNarrowText }
54842                 );
54843                 
54844                  
54845             }
54846             
54847             if(this.grid.enableColumnHide !== false){
54848
54849                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54850                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54851                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54852
54853                 this.hmenu.add('-',
54854                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54855                 );
54856             }
54857             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54858
54859             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54860         }
54861
54862         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54863             this.dd = new Roo.grid.GridDragZone(this.grid, {
54864                 ddGroup : this.grid.ddGroup || 'GridDD'
54865             });
54866             
54867         }
54868
54869         /*
54870         for(var i = 0; i < colCount; i++){
54871             if(cm.isHidden(i)){
54872                 this.hideColumn(i);
54873             }
54874             if(cm.config[i].align){
54875                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54876                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54877             }
54878         }*/
54879         
54880         this.updateHeaderSortState();
54881
54882         this.beforeInitialResize();
54883         this.layout(true);
54884
54885         // two part rendering gives faster view to the user
54886         this.renderPhase2.defer(1, this);
54887     },
54888
54889     renderPhase2 : function(){
54890         // render the rows now
54891         this.refresh();
54892         if(this.grid.autoSizeColumns){
54893             this.autoSizeColumns();
54894         }
54895     },
54896
54897     beforeInitialResize : function(){
54898
54899     },
54900
54901     onColumnSplitterMoved : function(i, w){
54902         this.userResized = true;
54903         var cm = this.grid.colModel;
54904         cm.setColumnWidth(i, w, true);
54905         var cid = cm.getColumnId(i);
54906         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54907         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54908         this.updateSplitters();
54909         this.layout();
54910         this.grid.fireEvent("columnresize", i, w);
54911     },
54912
54913     syncRowHeights : function(startIndex, endIndex){
54914         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54915             startIndex = startIndex || 0;
54916             var mrows = this.getBodyTable().rows;
54917             var lrows = this.getLockedTable().rows;
54918             var len = mrows.length-1;
54919             endIndex = Math.min(endIndex || len, len);
54920             for(var i = startIndex; i <= endIndex; i++){
54921                 var m = mrows[i], l = lrows[i];
54922                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54923                 m.style.height = l.style.height = h + "px";
54924             }
54925         }
54926     },
54927
54928     layout : function(initialRender, is2ndPass){
54929         var g = this.grid;
54930         var auto = g.autoHeight;
54931         var scrollOffset = 16;
54932         var c = g.getGridEl(), cm = this.cm,
54933                 expandCol = g.autoExpandColumn,
54934                 gv = this;
54935         //c.beginMeasure();
54936
54937         if(!c.dom.offsetWidth){ // display:none?
54938             if(initialRender){
54939                 this.lockedWrap.show();
54940                 this.mainWrap.show();
54941             }
54942             return;
54943         }
54944
54945         var hasLock = this.cm.isLocked(0);
54946
54947         var tbh = this.headerPanel.getHeight();
54948         var bbh = this.footerPanel.getHeight();
54949
54950         if(auto){
54951             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54952             var newHeight = ch + c.getBorderWidth("tb");
54953             if(g.maxHeight){
54954                 newHeight = Math.min(g.maxHeight, newHeight);
54955             }
54956             c.setHeight(newHeight);
54957         }
54958
54959         if(g.autoWidth){
54960             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54961         }
54962
54963         var s = this.scroller;
54964
54965         var csize = c.getSize(true);
54966
54967         this.el.setSize(csize.width, csize.height);
54968
54969         this.headerPanel.setWidth(csize.width);
54970         this.footerPanel.setWidth(csize.width);
54971
54972         var hdHeight = this.mainHd.getHeight();
54973         var vw = csize.width;
54974         var vh = csize.height - (tbh + bbh);
54975
54976         s.setSize(vw, vh);
54977
54978         var bt = this.getBodyTable();
54979         var ltWidth = hasLock ?
54980                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54981
54982         var scrollHeight = bt.offsetHeight;
54983         var scrollWidth = ltWidth + bt.offsetWidth;
54984         var vscroll = false, hscroll = false;
54985
54986         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54987
54988         var lw = this.lockedWrap, mw = this.mainWrap;
54989         var lb = this.lockedBody, mb = this.mainBody;
54990
54991         setTimeout(function(){
54992             var t = s.dom.offsetTop;
54993             var w = s.dom.clientWidth,
54994                 h = s.dom.clientHeight;
54995
54996             lw.setTop(t);
54997             lw.setSize(ltWidth, h);
54998
54999             mw.setLeftTop(ltWidth, t);
55000             mw.setSize(w-ltWidth, h);
55001
55002             lb.setHeight(h-hdHeight);
55003             mb.setHeight(h-hdHeight);
55004
55005             if(is2ndPass !== true && !gv.userResized && expandCol){
55006                 // high speed resize without full column calculation
55007                 
55008                 var ci = cm.getIndexById(expandCol);
55009                 if (ci < 0) {
55010                     ci = cm.findColumnIndex(expandCol);
55011                 }
55012                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55013                 var expandId = cm.getColumnId(ci);
55014                 var  tw = cm.getTotalWidth(false);
55015                 var currentWidth = cm.getColumnWidth(ci);
55016                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55017                 if(currentWidth != cw){
55018                     cm.setColumnWidth(ci, cw, true);
55019                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55020                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55021                     gv.updateSplitters();
55022                     gv.layout(false, true);
55023                 }
55024             }
55025
55026             if(initialRender){
55027                 lw.show();
55028                 mw.show();
55029             }
55030             //c.endMeasure();
55031         }, 10);
55032     },
55033
55034     onWindowResize : function(){
55035         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55036             return;
55037         }
55038         this.layout();
55039     },
55040
55041     appendFooter : function(parentEl){
55042         return null;
55043     },
55044
55045     sortAscText : "Sort Ascending",
55046     sortDescText : "Sort Descending",
55047     lockText : "Lock Column",
55048     unlockText : "Unlock Column",
55049     columnsText : "Columns",
55050  
55051     columnsWiderText : "Wider",
55052     columnsNarrowText : "Thinner"
55053 });
55054
55055
55056 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55057     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55058     this.proxy.el.addClass('x-grid3-col-dd');
55059 };
55060
55061 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55062     handleMouseDown : function(e){
55063
55064     },
55065
55066     callHandleMouseDown : function(e){
55067         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55068     }
55069 });
55070 /*
55071  * Based on:
55072  * Ext JS Library 1.1.1
55073  * Copyright(c) 2006-2007, Ext JS, LLC.
55074  *
55075  * Originally Released Under LGPL - original licence link has changed is not relivant.
55076  *
55077  * Fork - LGPL
55078  * <script type="text/javascript">
55079  */
55080  
55081 // private
55082 // This is a support class used internally by the Grid components
55083 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55084     this.grid = grid;
55085     this.view = grid.getView();
55086     this.proxy = this.view.resizeProxy;
55087     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55088         "gridSplitters" + this.grid.getGridEl().id, {
55089         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55090     });
55091     this.setHandleElId(Roo.id(hd));
55092     this.setOuterHandleElId(Roo.id(hd2));
55093     this.scroll = false;
55094 };
55095 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55096     fly: Roo.Element.fly,
55097
55098     b4StartDrag : function(x, y){
55099         this.view.headersDisabled = true;
55100         this.proxy.setHeight(this.view.mainWrap.getHeight());
55101         var w = this.cm.getColumnWidth(this.cellIndex);
55102         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55103         this.resetConstraints();
55104         this.setXConstraint(minw, 1000);
55105         this.setYConstraint(0, 0);
55106         this.minX = x - minw;
55107         this.maxX = x + 1000;
55108         this.startPos = x;
55109         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55110     },
55111
55112
55113     handleMouseDown : function(e){
55114         ev = Roo.EventObject.setEvent(e);
55115         var t = this.fly(ev.getTarget());
55116         if(t.hasClass("x-grid-split")){
55117             this.cellIndex = this.view.getCellIndex(t.dom);
55118             this.split = t.dom;
55119             this.cm = this.grid.colModel;
55120             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55121                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55122             }
55123         }
55124     },
55125
55126     endDrag : function(e){
55127         this.view.headersDisabled = false;
55128         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55129         var diff = endX - this.startPos;
55130         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55131     },
55132
55133     autoOffset : function(){
55134         this.setDelta(0,0);
55135     }
55136 });/*
55137  * Based on:
55138  * Ext JS Library 1.1.1
55139  * Copyright(c) 2006-2007, Ext JS, LLC.
55140  *
55141  * Originally Released Under LGPL - original licence link has changed is not relivant.
55142  *
55143  * Fork - LGPL
55144  * <script type="text/javascript">
55145  */
55146  
55147 // private
55148 // This is a support class used internally by the Grid components
55149 Roo.grid.GridDragZone = function(grid, config){
55150     this.view = grid.getView();
55151     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55152     if(this.view.lockedBody){
55153         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55154         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55155     }
55156     this.scroll = false;
55157     this.grid = grid;
55158     this.ddel = document.createElement('div');
55159     this.ddel.className = 'x-grid-dd-wrap';
55160 };
55161
55162 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55163     ddGroup : "GridDD",
55164
55165     getDragData : function(e){
55166         var t = Roo.lib.Event.getTarget(e);
55167         var rowIndex = this.view.findRowIndex(t);
55168         var sm = this.grid.selModel;
55169             
55170         //Roo.log(rowIndex);
55171         
55172         if (sm.getSelectedCell) {
55173             // cell selection..
55174             if (!sm.getSelectedCell()) {
55175                 return false;
55176             }
55177             if (rowIndex != sm.getSelectedCell()[0]) {
55178                 return false;
55179             }
55180         
55181         }
55182         
55183         if(rowIndex !== false){
55184             
55185             // if editorgrid.. 
55186             
55187             
55188             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55189                
55190             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55191               //  
55192             //}
55193             if (e.hasModifier()){
55194                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55195             }
55196             
55197             Roo.log("getDragData");
55198             
55199             return {
55200                 grid: this.grid,
55201                 ddel: this.ddel,
55202                 rowIndex: rowIndex,
55203                 selections:sm.getSelections ? sm.getSelections() : (
55204                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55205                 )
55206             };
55207         }
55208         return false;
55209     },
55210
55211     onInitDrag : function(e){
55212         var data = this.dragData;
55213         this.ddel.innerHTML = this.grid.getDragDropText();
55214         this.proxy.update(this.ddel);
55215         // fire start drag?
55216     },
55217
55218     afterRepair : function(){
55219         this.dragging = false;
55220     },
55221
55222     getRepairXY : function(e, data){
55223         return false;
55224     },
55225
55226     onEndDrag : function(data, e){
55227         // fire end drag?
55228     },
55229
55230     onValidDrop : function(dd, e, id){
55231         // fire drag drop?
55232         this.hideProxy();
55233     },
55234
55235     beforeInvalidDrop : function(e, id){
55236
55237     }
55238 });/*
55239  * Based on:
55240  * Ext JS Library 1.1.1
55241  * Copyright(c) 2006-2007, Ext JS, LLC.
55242  *
55243  * Originally Released Under LGPL - original licence link has changed is not relivant.
55244  *
55245  * Fork - LGPL
55246  * <script type="text/javascript">
55247  */
55248  
55249
55250 /**
55251  * @class Roo.grid.ColumnModel
55252  * @extends Roo.util.Observable
55253  * This is the default implementation of a ColumnModel used by the Grid. It defines
55254  * the columns in the grid.
55255  * <br>Usage:<br>
55256  <pre><code>
55257  var colModel = new Roo.grid.ColumnModel([
55258         {header: "Ticker", width: 60, sortable: true, locked: true},
55259         {header: "Company Name", width: 150, sortable: true},
55260         {header: "Market Cap.", width: 100, sortable: true},
55261         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55262         {header: "Employees", width: 100, sortable: true, resizable: false}
55263  ]);
55264  </code></pre>
55265  * <p>
55266  
55267  * The config options listed for this class are options which may appear in each
55268  * individual column definition.
55269  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55270  * @constructor
55271  * @param {Object} config An Array of column config objects. See this class's
55272  * config objects for details.
55273 */
55274 Roo.grid.ColumnModel = function(config){
55275         /**
55276      * The config passed into the constructor
55277      */
55278     this.config = config;
55279     this.lookup = {};
55280
55281     // if no id, create one
55282     // if the column does not have a dataIndex mapping,
55283     // map it to the order it is in the config
55284     for(var i = 0, len = config.length; i < len; i++){
55285         var c = config[i];
55286         if(typeof c.dataIndex == "undefined"){
55287             c.dataIndex = i;
55288         }
55289         if(typeof c.renderer == "string"){
55290             c.renderer = Roo.util.Format[c.renderer];
55291         }
55292         if(typeof c.id == "undefined"){
55293             c.id = Roo.id();
55294         }
55295         if(c.editor && c.editor.xtype){
55296             c.editor  = Roo.factory(c.editor, Roo.grid);
55297         }
55298         if(c.editor && c.editor.isFormField){
55299             c.editor = new Roo.grid.GridEditor(c.editor);
55300         }
55301         this.lookup[c.id] = c;
55302     }
55303
55304     /**
55305      * The width of columns which have no width specified (defaults to 100)
55306      * @type Number
55307      */
55308     this.defaultWidth = 100;
55309
55310     /**
55311      * Default sortable of columns which have no sortable specified (defaults to false)
55312      * @type Boolean
55313      */
55314     this.defaultSortable = false;
55315
55316     this.addEvents({
55317         /**
55318              * @event widthchange
55319              * Fires when the width of a column changes.
55320              * @param {ColumnModel} this
55321              * @param {Number} columnIndex The column index
55322              * @param {Number} newWidth The new width
55323              */
55324             "widthchange": true,
55325         /**
55326              * @event headerchange
55327              * Fires when the text of a header changes.
55328              * @param {ColumnModel} this
55329              * @param {Number} columnIndex The column index
55330              * @param {Number} newText The new header text
55331              */
55332             "headerchange": true,
55333         /**
55334              * @event hiddenchange
55335              * Fires when a column is hidden or "unhidden".
55336              * @param {ColumnModel} this
55337              * @param {Number} columnIndex The column index
55338              * @param {Boolean} hidden true if hidden, false otherwise
55339              */
55340             "hiddenchange": true,
55341             /**
55342          * @event columnmoved
55343          * Fires when a column is moved.
55344          * @param {ColumnModel} this
55345          * @param {Number} oldIndex
55346          * @param {Number} newIndex
55347          */
55348         "columnmoved" : true,
55349         /**
55350          * @event columlockchange
55351          * Fires when a column's locked state is changed
55352          * @param {ColumnModel} this
55353          * @param {Number} colIndex
55354          * @param {Boolean} locked true if locked
55355          */
55356         "columnlockchange" : true
55357     });
55358     Roo.grid.ColumnModel.superclass.constructor.call(this);
55359 };
55360 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55361     /**
55362      * @cfg {String} header The header text to display in the Grid view.
55363      */
55364     /**
55365      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55366      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55367      * specified, the column's index is used as an index into the Record's data Array.
55368      */
55369     /**
55370      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55371      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55372      */
55373     /**
55374      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55375      * Defaults to the value of the {@link #defaultSortable} property.
55376      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55377      */
55378     /**
55379      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55380      */
55381     /**
55382      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55383      */
55384     /**
55385      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55386      */
55387     /**
55388      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55389      */
55390     /**
55391      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55392      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55393      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55394      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55395      */
55396        /**
55397      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55398      */
55399     /**
55400      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55401      */
55402     /**
55403      * @cfg {String} cursor (Optional)
55404      */
55405     /**
55406      * @cfg {String} tooltip (Optional)
55407      */
55408     /**
55409      * Returns the id of the column at the specified index.
55410      * @param {Number} index The column index
55411      * @return {String} the id
55412      */
55413     getColumnId : function(index){
55414         return this.config[index].id;
55415     },
55416
55417     /**
55418      * Returns the column for a specified id.
55419      * @param {String} id The column id
55420      * @return {Object} the column
55421      */
55422     getColumnById : function(id){
55423         return this.lookup[id];
55424     },
55425
55426     
55427     /**
55428      * Returns the column for a specified dataIndex.
55429      * @param {String} dataIndex The column dataIndex
55430      * @return {Object|Boolean} the column or false if not found
55431      */
55432     getColumnByDataIndex: function(dataIndex){
55433         var index = this.findColumnIndex(dataIndex);
55434         return index > -1 ? this.config[index] : false;
55435     },
55436     
55437     /**
55438      * Returns the index for a specified column id.
55439      * @param {String} id The column id
55440      * @return {Number} the index, or -1 if not found
55441      */
55442     getIndexById : function(id){
55443         for(var i = 0, len = this.config.length; i < len; i++){
55444             if(this.config[i].id == id){
55445                 return i;
55446             }
55447         }
55448         return -1;
55449     },
55450     
55451     /**
55452      * Returns the index for a specified column dataIndex.
55453      * @param {String} dataIndex The column dataIndex
55454      * @return {Number} the index, or -1 if not found
55455      */
55456     
55457     findColumnIndex : function(dataIndex){
55458         for(var i = 0, len = this.config.length; i < len; i++){
55459             if(this.config[i].dataIndex == dataIndex){
55460                 return i;
55461             }
55462         }
55463         return -1;
55464     },
55465     
55466     
55467     moveColumn : function(oldIndex, newIndex){
55468         var c = this.config[oldIndex];
55469         this.config.splice(oldIndex, 1);
55470         this.config.splice(newIndex, 0, c);
55471         this.dataMap = null;
55472         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55473     },
55474
55475     isLocked : function(colIndex){
55476         return this.config[colIndex].locked === true;
55477     },
55478
55479     setLocked : function(colIndex, value, suppressEvent){
55480         if(this.isLocked(colIndex) == value){
55481             return;
55482         }
55483         this.config[colIndex].locked = value;
55484         if(!suppressEvent){
55485             this.fireEvent("columnlockchange", this, colIndex, value);
55486         }
55487     },
55488
55489     getTotalLockedWidth : function(){
55490         var totalWidth = 0;
55491         for(var i = 0; i < this.config.length; i++){
55492             if(this.isLocked(i) && !this.isHidden(i)){
55493                 this.totalWidth += this.getColumnWidth(i);
55494             }
55495         }
55496         return totalWidth;
55497     },
55498
55499     getLockedCount : function(){
55500         for(var i = 0, len = this.config.length; i < len; i++){
55501             if(!this.isLocked(i)){
55502                 return i;
55503             }
55504         }
55505     },
55506
55507     /**
55508      * Returns the number of columns.
55509      * @return {Number}
55510      */
55511     getColumnCount : function(visibleOnly){
55512         if(visibleOnly === true){
55513             var c = 0;
55514             for(var i = 0, len = this.config.length; i < len; i++){
55515                 if(!this.isHidden(i)){
55516                     c++;
55517                 }
55518             }
55519             return c;
55520         }
55521         return this.config.length;
55522     },
55523
55524     /**
55525      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55526      * @param {Function} fn
55527      * @param {Object} scope (optional)
55528      * @return {Array} result
55529      */
55530     getColumnsBy : function(fn, scope){
55531         var r = [];
55532         for(var i = 0, len = this.config.length; i < len; i++){
55533             var c = this.config[i];
55534             if(fn.call(scope||this, c, i) === true){
55535                 r[r.length] = c;
55536             }
55537         }
55538         return r;
55539     },
55540
55541     /**
55542      * Returns true if the specified column is sortable.
55543      * @param {Number} col The column index
55544      * @return {Boolean}
55545      */
55546     isSortable : function(col){
55547         if(typeof this.config[col].sortable == "undefined"){
55548             return this.defaultSortable;
55549         }
55550         return this.config[col].sortable;
55551     },
55552
55553     /**
55554      * Returns the rendering (formatting) function defined for the column.
55555      * @param {Number} col The column index.
55556      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55557      */
55558     getRenderer : function(col){
55559         if(!this.config[col].renderer){
55560             return Roo.grid.ColumnModel.defaultRenderer;
55561         }
55562         return this.config[col].renderer;
55563     },
55564
55565     /**
55566      * Sets the rendering (formatting) function for a column.
55567      * @param {Number} col The column index
55568      * @param {Function} fn The function to use to process the cell's raw data
55569      * to return HTML markup for the grid view. The render function is called with
55570      * the following parameters:<ul>
55571      * <li>Data value.</li>
55572      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55573      * <li>css A CSS style string to apply to the table cell.</li>
55574      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55575      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55576      * <li>Row index</li>
55577      * <li>Column index</li>
55578      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55579      */
55580     setRenderer : function(col, fn){
55581         this.config[col].renderer = fn;
55582     },
55583
55584     /**
55585      * Returns the width for the specified column.
55586      * @param {Number} col The column index
55587      * @return {Number}
55588      */
55589     getColumnWidth : function(col){
55590         return this.config[col].width * 1 || this.defaultWidth;
55591     },
55592
55593     /**
55594      * Sets the width for a column.
55595      * @param {Number} col The column index
55596      * @param {Number} width The new width
55597      */
55598     setColumnWidth : function(col, width, suppressEvent){
55599         this.config[col].width = width;
55600         this.totalWidth = null;
55601         if(!suppressEvent){
55602              this.fireEvent("widthchange", this, col, width);
55603         }
55604     },
55605
55606     /**
55607      * Returns the total width of all columns.
55608      * @param {Boolean} includeHidden True to include hidden column widths
55609      * @return {Number}
55610      */
55611     getTotalWidth : function(includeHidden){
55612         if(!this.totalWidth){
55613             this.totalWidth = 0;
55614             for(var i = 0, len = this.config.length; i < len; i++){
55615                 if(includeHidden || !this.isHidden(i)){
55616                     this.totalWidth += this.getColumnWidth(i);
55617                 }
55618             }
55619         }
55620         return this.totalWidth;
55621     },
55622
55623     /**
55624      * Returns the header for the specified column.
55625      * @param {Number} col The column index
55626      * @return {String}
55627      */
55628     getColumnHeader : function(col){
55629         return this.config[col].header;
55630     },
55631
55632     /**
55633      * Sets the header for a column.
55634      * @param {Number} col The column index
55635      * @param {String} header The new header
55636      */
55637     setColumnHeader : function(col, header){
55638         this.config[col].header = header;
55639         this.fireEvent("headerchange", this, col, header);
55640     },
55641
55642     /**
55643      * Returns the tooltip for the specified column.
55644      * @param {Number} col The column index
55645      * @return {String}
55646      */
55647     getColumnTooltip : function(col){
55648             return this.config[col].tooltip;
55649     },
55650     /**
55651      * Sets the tooltip for a column.
55652      * @param {Number} col The column index
55653      * @param {String} tooltip The new tooltip
55654      */
55655     setColumnTooltip : function(col, tooltip){
55656             this.config[col].tooltip = tooltip;
55657     },
55658
55659     /**
55660      * Returns the dataIndex for the specified column.
55661      * @param {Number} col The column index
55662      * @return {Number}
55663      */
55664     getDataIndex : function(col){
55665         return this.config[col].dataIndex;
55666     },
55667
55668     /**
55669      * Sets the dataIndex for a column.
55670      * @param {Number} col The column index
55671      * @param {Number} dataIndex The new dataIndex
55672      */
55673     setDataIndex : function(col, dataIndex){
55674         this.config[col].dataIndex = dataIndex;
55675     },
55676
55677     
55678     
55679     /**
55680      * Returns true if the cell is editable.
55681      * @param {Number} colIndex The column index
55682      * @param {Number} rowIndex The row index
55683      * @return {Boolean}
55684      */
55685     isCellEditable : function(colIndex, rowIndex){
55686         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55687     },
55688
55689     /**
55690      * Returns the editor defined for the cell/column.
55691      * return false or null to disable editing.
55692      * @param {Number} colIndex The column index
55693      * @param {Number} rowIndex The row index
55694      * @return {Object}
55695      */
55696     getCellEditor : function(colIndex, rowIndex){
55697         return this.config[colIndex].editor;
55698     },
55699
55700     /**
55701      * Sets if a column is editable.
55702      * @param {Number} col The column index
55703      * @param {Boolean} editable True if the column is editable
55704      */
55705     setEditable : function(col, editable){
55706         this.config[col].editable = editable;
55707     },
55708
55709
55710     /**
55711      * Returns true if the column is hidden.
55712      * @param {Number} colIndex The column index
55713      * @return {Boolean}
55714      */
55715     isHidden : function(colIndex){
55716         return this.config[colIndex].hidden;
55717     },
55718
55719
55720     /**
55721      * Returns true if the column width cannot be changed
55722      */
55723     isFixed : function(colIndex){
55724         return this.config[colIndex].fixed;
55725     },
55726
55727     /**
55728      * Returns true if the column can be resized
55729      * @return {Boolean}
55730      */
55731     isResizable : function(colIndex){
55732         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55733     },
55734     /**
55735      * Sets if a column is hidden.
55736      * @param {Number} colIndex The column index
55737      * @param {Boolean} hidden True if the column is hidden
55738      */
55739     setHidden : function(colIndex, hidden){
55740         this.config[colIndex].hidden = hidden;
55741         this.totalWidth = null;
55742         this.fireEvent("hiddenchange", this, colIndex, hidden);
55743     },
55744
55745     /**
55746      * Sets the editor for a column.
55747      * @param {Number} col The column index
55748      * @param {Object} editor The editor object
55749      */
55750     setEditor : function(col, editor){
55751         this.config[col].editor = editor;
55752     }
55753 });
55754
55755 Roo.grid.ColumnModel.defaultRenderer = function(value){
55756         if(typeof value == "string" && value.length < 1){
55757             return "&#160;";
55758         }
55759         return value;
55760 };
55761
55762 // Alias for backwards compatibility
55763 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55764 /*
55765  * Based on:
55766  * Ext JS Library 1.1.1
55767  * Copyright(c) 2006-2007, Ext JS, LLC.
55768  *
55769  * Originally Released Under LGPL - original licence link has changed is not relivant.
55770  *
55771  * Fork - LGPL
55772  * <script type="text/javascript">
55773  */
55774
55775 /**
55776  * @class Roo.grid.AbstractSelectionModel
55777  * @extends Roo.util.Observable
55778  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55779  * implemented by descendant classes.  This class should not be directly instantiated.
55780  * @constructor
55781  */
55782 Roo.grid.AbstractSelectionModel = function(){
55783     this.locked = false;
55784     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55785 };
55786
55787 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55788     /** @ignore Called by the grid automatically. Do not call directly. */
55789     init : function(grid){
55790         this.grid = grid;
55791         this.initEvents();
55792     },
55793
55794     /**
55795      * Locks the selections.
55796      */
55797     lock : function(){
55798         this.locked = true;
55799     },
55800
55801     /**
55802      * Unlocks the selections.
55803      */
55804     unlock : function(){
55805         this.locked = false;
55806     },
55807
55808     /**
55809      * Returns true if the selections are locked.
55810      * @return {Boolean}
55811      */
55812     isLocked : function(){
55813         return this.locked;
55814     }
55815 });/*
55816  * Based on:
55817  * Ext JS Library 1.1.1
55818  * Copyright(c) 2006-2007, Ext JS, LLC.
55819  *
55820  * Originally Released Under LGPL - original licence link has changed is not relivant.
55821  *
55822  * Fork - LGPL
55823  * <script type="text/javascript">
55824  */
55825 /**
55826  * @extends Roo.grid.AbstractSelectionModel
55827  * @class Roo.grid.RowSelectionModel
55828  * The default SelectionModel used by {@link Roo.grid.Grid}.
55829  * It supports multiple selections and keyboard selection/navigation. 
55830  * @constructor
55831  * @param {Object} config
55832  */
55833 Roo.grid.RowSelectionModel = function(config){
55834     Roo.apply(this, config);
55835     this.selections = new Roo.util.MixedCollection(false, function(o){
55836         return o.id;
55837     });
55838
55839     this.last = false;
55840     this.lastActive = false;
55841
55842     this.addEvents({
55843         /**
55844              * @event selectionchange
55845              * Fires when the selection changes
55846              * @param {SelectionModel} this
55847              */
55848             "selectionchange" : true,
55849         /**
55850              * @event afterselectionchange
55851              * Fires after the selection changes (eg. by key press or clicking)
55852              * @param {SelectionModel} this
55853              */
55854             "afterselectionchange" : true,
55855         /**
55856              * @event beforerowselect
55857              * Fires when a row is selected being selected, return false to cancel.
55858              * @param {SelectionModel} this
55859              * @param {Number} rowIndex The selected index
55860              * @param {Boolean} keepExisting False if other selections will be cleared
55861              */
55862             "beforerowselect" : true,
55863         /**
55864              * @event rowselect
55865              * Fires when a row is selected.
55866              * @param {SelectionModel} this
55867              * @param {Number} rowIndex The selected index
55868              * @param {Roo.data.Record} r The record
55869              */
55870             "rowselect" : true,
55871         /**
55872              * @event rowdeselect
55873              * Fires when a row is deselected.
55874              * @param {SelectionModel} this
55875              * @param {Number} rowIndex The selected index
55876              */
55877         "rowdeselect" : true
55878     });
55879     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55880     this.locked = false;
55881 };
55882
55883 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55884     /**
55885      * @cfg {Boolean} singleSelect
55886      * True to allow selection of only one row at a time (defaults to false)
55887      */
55888     singleSelect : false,
55889
55890     // private
55891     initEvents : function(){
55892
55893         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55894             this.grid.on("mousedown", this.handleMouseDown, this);
55895         }else{ // allow click to work like normal
55896             this.grid.on("rowclick", this.handleDragableRowClick, this);
55897         }
55898
55899         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55900             "up" : function(e){
55901                 if(!e.shiftKey){
55902                     this.selectPrevious(e.shiftKey);
55903                 }else if(this.last !== false && this.lastActive !== false){
55904                     var last = this.last;
55905                     this.selectRange(this.last,  this.lastActive-1);
55906                     this.grid.getView().focusRow(this.lastActive);
55907                     if(last !== false){
55908                         this.last = last;
55909                     }
55910                 }else{
55911                     this.selectFirstRow();
55912                 }
55913                 this.fireEvent("afterselectionchange", this);
55914             },
55915             "down" : function(e){
55916                 if(!e.shiftKey){
55917                     this.selectNext(e.shiftKey);
55918                 }else if(this.last !== false && this.lastActive !== false){
55919                     var last = this.last;
55920                     this.selectRange(this.last,  this.lastActive+1);
55921                     this.grid.getView().focusRow(this.lastActive);
55922                     if(last !== false){
55923                         this.last = last;
55924                     }
55925                 }else{
55926                     this.selectFirstRow();
55927                 }
55928                 this.fireEvent("afterselectionchange", this);
55929             },
55930             scope: this
55931         });
55932
55933         var view = this.grid.view;
55934         view.on("refresh", this.onRefresh, this);
55935         view.on("rowupdated", this.onRowUpdated, this);
55936         view.on("rowremoved", this.onRemove, this);
55937     },
55938
55939     // private
55940     onRefresh : function(){
55941         var ds = this.grid.dataSource, i, v = this.grid.view;
55942         var s = this.selections;
55943         s.each(function(r){
55944             if((i = ds.indexOfId(r.id)) != -1){
55945                 v.onRowSelect(i);
55946                 s.add(ds.getAt(i)); // updating the selection relate data
55947             }else{
55948                 s.remove(r);
55949             }
55950         });
55951     },
55952
55953     // private
55954     onRemove : function(v, index, r){
55955         this.selections.remove(r);
55956     },
55957
55958     // private
55959     onRowUpdated : function(v, index, r){
55960         if(this.isSelected(r)){
55961             v.onRowSelect(index);
55962         }
55963     },
55964
55965     /**
55966      * Select records.
55967      * @param {Array} records The records to select
55968      * @param {Boolean} keepExisting (optional) True to keep existing selections
55969      */
55970     selectRecords : function(records, keepExisting){
55971         if(!keepExisting){
55972             this.clearSelections();
55973         }
55974         var ds = this.grid.dataSource;
55975         for(var i = 0, len = records.length; i < len; i++){
55976             this.selectRow(ds.indexOf(records[i]), true);
55977         }
55978     },
55979
55980     /**
55981      * Gets the number of selected rows.
55982      * @return {Number}
55983      */
55984     getCount : function(){
55985         return this.selections.length;
55986     },
55987
55988     /**
55989      * Selects the first row in the grid.
55990      */
55991     selectFirstRow : function(){
55992         this.selectRow(0);
55993     },
55994
55995     /**
55996      * Select the last row.
55997      * @param {Boolean} keepExisting (optional) True to keep existing selections
55998      */
55999     selectLastRow : function(keepExisting){
56000         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
56001     },
56002
56003     /**
56004      * Selects the row immediately following the last selected row.
56005      * @param {Boolean} keepExisting (optional) True to keep existing selections
56006      */
56007     selectNext : function(keepExisting){
56008         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
56009             this.selectRow(this.last+1, keepExisting);
56010             this.grid.getView().focusRow(this.last);
56011         }
56012     },
56013
56014     /**
56015      * Selects the row that precedes the last selected row.
56016      * @param {Boolean} keepExisting (optional) True to keep existing selections
56017      */
56018     selectPrevious : function(keepExisting){
56019         if(this.last){
56020             this.selectRow(this.last-1, keepExisting);
56021             this.grid.getView().focusRow(this.last);
56022         }
56023     },
56024
56025     /**
56026      * Returns the selected records
56027      * @return {Array} Array of selected records
56028      */
56029     getSelections : function(){
56030         return [].concat(this.selections.items);
56031     },
56032
56033     /**
56034      * Returns the first selected record.
56035      * @return {Record}
56036      */
56037     getSelected : function(){
56038         return this.selections.itemAt(0);
56039     },
56040
56041
56042     /**
56043      * Clears all selections.
56044      */
56045     clearSelections : function(fast){
56046         if(this.locked) return;
56047         if(fast !== true){
56048             var ds = this.grid.dataSource;
56049             var s = this.selections;
56050             s.each(function(r){
56051                 this.deselectRow(ds.indexOfId(r.id));
56052             }, this);
56053             s.clear();
56054         }else{
56055             this.selections.clear();
56056         }
56057         this.last = false;
56058     },
56059
56060
56061     /**
56062      * Selects all rows.
56063      */
56064     selectAll : function(){
56065         if(this.locked) return;
56066         this.selections.clear();
56067         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56068             this.selectRow(i, true);
56069         }
56070     },
56071
56072     /**
56073      * Returns True if there is a selection.
56074      * @return {Boolean}
56075      */
56076     hasSelection : function(){
56077         return this.selections.length > 0;
56078     },
56079
56080     /**
56081      * Returns True if the specified row is selected.
56082      * @param {Number/Record} record The record or index of the record to check
56083      * @return {Boolean}
56084      */
56085     isSelected : function(index){
56086         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56087         return (r && this.selections.key(r.id) ? true : false);
56088     },
56089
56090     /**
56091      * Returns True if the specified record id is selected.
56092      * @param {String} id The id of record to check
56093      * @return {Boolean}
56094      */
56095     isIdSelected : function(id){
56096         return (this.selections.key(id) ? true : false);
56097     },
56098
56099     // private
56100     handleMouseDown : function(e, t){
56101         var view = this.grid.getView(), rowIndex;
56102         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56103             return;
56104         };
56105         if(e.shiftKey && this.last !== false){
56106             var last = this.last;
56107             this.selectRange(last, rowIndex, e.ctrlKey);
56108             this.last = last; // reset the last
56109             view.focusRow(rowIndex);
56110         }else{
56111             var isSelected = this.isSelected(rowIndex);
56112             if(e.button !== 0 && isSelected){
56113                 view.focusRow(rowIndex);
56114             }else if(e.ctrlKey && isSelected){
56115                 this.deselectRow(rowIndex);
56116             }else if(!isSelected){
56117                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56118                 view.focusRow(rowIndex);
56119             }
56120         }
56121         this.fireEvent("afterselectionchange", this);
56122     },
56123     // private
56124     handleDragableRowClick :  function(grid, rowIndex, e) 
56125     {
56126         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56127             this.selectRow(rowIndex, false);
56128             grid.view.focusRow(rowIndex);
56129              this.fireEvent("afterselectionchange", this);
56130         }
56131     },
56132     
56133     /**
56134      * Selects multiple rows.
56135      * @param {Array} rows Array of the indexes of the row to select
56136      * @param {Boolean} keepExisting (optional) True to keep existing selections
56137      */
56138     selectRows : function(rows, keepExisting){
56139         if(!keepExisting){
56140             this.clearSelections();
56141         }
56142         for(var i = 0, len = rows.length; i < len; i++){
56143             this.selectRow(rows[i], true);
56144         }
56145     },
56146
56147     /**
56148      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56149      * @param {Number} startRow The index of the first row in the range
56150      * @param {Number} endRow The index of the last row in the range
56151      * @param {Boolean} keepExisting (optional) True to retain existing selections
56152      */
56153     selectRange : function(startRow, endRow, keepExisting){
56154         if(this.locked) return;
56155         if(!keepExisting){
56156             this.clearSelections();
56157         }
56158         if(startRow <= endRow){
56159             for(var i = startRow; i <= endRow; i++){
56160                 this.selectRow(i, true);
56161             }
56162         }else{
56163             for(var i = startRow; i >= endRow; i--){
56164                 this.selectRow(i, true);
56165             }
56166         }
56167     },
56168
56169     /**
56170      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56171      * @param {Number} startRow The index of the first row in the range
56172      * @param {Number} endRow The index of the last row in the range
56173      */
56174     deselectRange : function(startRow, endRow, preventViewNotify){
56175         if(this.locked) return;
56176         for(var i = startRow; i <= endRow; i++){
56177             this.deselectRow(i, preventViewNotify);
56178         }
56179     },
56180
56181     /**
56182      * Selects a row.
56183      * @param {Number} row The index of the row to select
56184      * @param {Boolean} keepExisting (optional) True to keep existing selections
56185      */
56186     selectRow : function(index, keepExisting, preventViewNotify){
56187         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56188         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56189             if(!keepExisting || this.singleSelect){
56190                 this.clearSelections();
56191             }
56192             var r = this.grid.dataSource.getAt(index);
56193             this.selections.add(r);
56194             this.last = this.lastActive = index;
56195             if(!preventViewNotify){
56196                 this.grid.getView().onRowSelect(index);
56197             }
56198             this.fireEvent("rowselect", this, index, r);
56199             this.fireEvent("selectionchange", this);
56200         }
56201     },
56202
56203     /**
56204      * Deselects a row.
56205      * @param {Number} row The index of the row to deselect
56206      */
56207     deselectRow : function(index, preventViewNotify){
56208         if(this.locked) return;
56209         if(this.last == index){
56210             this.last = false;
56211         }
56212         if(this.lastActive == index){
56213             this.lastActive = false;
56214         }
56215         var r = this.grid.dataSource.getAt(index);
56216         this.selections.remove(r);
56217         if(!preventViewNotify){
56218             this.grid.getView().onRowDeselect(index);
56219         }
56220         this.fireEvent("rowdeselect", this, index);
56221         this.fireEvent("selectionchange", this);
56222     },
56223
56224     // private
56225     restoreLast : function(){
56226         if(this._last){
56227             this.last = this._last;
56228         }
56229     },
56230
56231     // private
56232     acceptsNav : function(row, col, cm){
56233         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56234     },
56235
56236     // private
56237     onEditorKey : function(field, e){
56238         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56239         if(k == e.TAB){
56240             e.stopEvent();
56241             ed.completeEdit();
56242             if(e.shiftKey){
56243                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56244             }else{
56245                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56246             }
56247         }else if(k == e.ENTER && !e.ctrlKey){
56248             e.stopEvent();
56249             ed.completeEdit();
56250             if(e.shiftKey){
56251                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56252             }else{
56253                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56254             }
56255         }else if(k == e.ESC){
56256             ed.cancelEdit();
56257         }
56258         if(newCell){
56259             g.startEditing(newCell[0], newCell[1]);
56260         }
56261     }
56262 });/*
56263  * Based on:
56264  * Ext JS Library 1.1.1
56265  * Copyright(c) 2006-2007, Ext JS, LLC.
56266  *
56267  * Originally Released Under LGPL - original licence link has changed is not relivant.
56268  *
56269  * Fork - LGPL
56270  * <script type="text/javascript">
56271  */
56272 /**
56273  * @class Roo.grid.CellSelectionModel
56274  * @extends Roo.grid.AbstractSelectionModel
56275  * This class provides the basic implementation for cell selection in a grid.
56276  * @constructor
56277  * @param {Object} config The object containing the configuration of this model.
56278  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56279  */
56280 Roo.grid.CellSelectionModel = function(config){
56281     Roo.apply(this, config);
56282
56283     this.selection = null;
56284
56285     this.addEvents({
56286         /**
56287              * @event beforerowselect
56288              * Fires before a cell is selected.
56289              * @param {SelectionModel} this
56290              * @param {Number} rowIndex The selected row index
56291              * @param {Number} colIndex The selected cell index
56292              */
56293             "beforecellselect" : true,
56294         /**
56295              * @event cellselect
56296              * Fires when a cell is selected.
56297              * @param {SelectionModel} this
56298              * @param {Number} rowIndex The selected row index
56299              * @param {Number} colIndex The selected cell index
56300              */
56301             "cellselect" : true,
56302         /**
56303              * @event selectionchange
56304              * Fires when the active selection changes.
56305              * @param {SelectionModel} this
56306              * @param {Object} selection null for no selection or an object (o) with two properties
56307                 <ul>
56308                 <li>o.record: the record object for the row the selection is in</li>
56309                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56310                 </ul>
56311              */
56312             "selectionchange" : true,
56313         /**
56314              * @event tabend
56315              * Fires when the tab (or enter) was pressed on the last editable cell
56316              * You can use this to trigger add new row.
56317              * @param {SelectionModel} this
56318              */
56319             "tabend" : true,
56320          /**
56321              * @event beforeeditnext
56322              * Fires before the next editable sell is made active
56323              * You can use this to skip to another cell or fire the tabend
56324              *    if you set cell to false
56325              * @param {Object} eventdata object : { cell : [ row, col ] } 
56326              */
56327             "beforeeditnext" : true
56328     });
56329     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56330 };
56331
56332 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56333     
56334     enter_is_tab: false,
56335
56336     /** @ignore */
56337     initEvents : function(){
56338         this.grid.on("mousedown", this.handleMouseDown, this);
56339         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56340         var view = this.grid.view;
56341         view.on("refresh", this.onViewChange, this);
56342         view.on("rowupdated", this.onRowUpdated, this);
56343         view.on("beforerowremoved", this.clearSelections, this);
56344         view.on("beforerowsinserted", this.clearSelections, this);
56345         if(this.grid.isEditor){
56346             this.grid.on("beforeedit", this.beforeEdit,  this);
56347         }
56348     },
56349
56350         //private
56351     beforeEdit : function(e){
56352         this.select(e.row, e.column, false, true, e.record);
56353     },
56354
56355         //private
56356     onRowUpdated : function(v, index, r){
56357         if(this.selection && this.selection.record == r){
56358             v.onCellSelect(index, this.selection.cell[1]);
56359         }
56360     },
56361
56362         //private
56363     onViewChange : function(){
56364         this.clearSelections(true);
56365     },
56366
56367         /**
56368          * Returns the currently selected cell,.
56369          * @return {Array} The selected cell (row, column) or null if none selected.
56370          */
56371     getSelectedCell : function(){
56372         return this.selection ? this.selection.cell : null;
56373     },
56374
56375     /**
56376      * Clears all selections.
56377      * @param {Boolean} true to prevent the gridview from being notified about the change.
56378      */
56379     clearSelections : function(preventNotify){
56380         var s = this.selection;
56381         if(s){
56382             if(preventNotify !== true){
56383                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56384             }
56385             this.selection = null;
56386             this.fireEvent("selectionchange", this, null);
56387         }
56388     },
56389
56390     /**
56391      * Returns true if there is a selection.
56392      * @return {Boolean}
56393      */
56394     hasSelection : function(){
56395         return this.selection ? true : false;
56396     },
56397
56398     /** @ignore */
56399     handleMouseDown : function(e, t){
56400         var v = this.grid.getView();
56401         if(this.isLocked()){
56402             return;
56403         };
56404         var row = v.findRowIndex(t);
56405         var cell = v.findCellIndex(t);
56406         if(row !== false && cell !== false){
56407             this.select(row, cell);
56408         }
56409     },
56410
56411     /**
56412      * Selects a cell.
56413      * @param {Number} rowIndex
56414      * @param {Number} collIndex
56415      */
56416     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56417         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56418             this.clearSelections();
56419             r = r || this.grid.dataSource.getAt(rowIndex);
56420             this.selection = {
56421                 record : r,
56422                 cell : [rowIndex, colIndex]
56423             };
56424             if(!preventViewNotify){
56425                 var v = this.grid.getView();
56426                 v.onCellSelect(rowIndex, colIndex);
56427                 if(preventFocus !== true){
56428                     v.focusCell(rowIndex, colIndex);
56429                 }
56430             }
56431             this.fireEvent("cellselect", this, rowIndex, colIndex);
56432             this.fireEvent("selectionchange", this, this.selection);
56433         }
56434     },
56435
56436         //private
56437     isSelectable : function(rowIndex, colIndex, cm){
56438         return !cm.isHidden(colIndex);
56439     },
56440
56441     /** @ignore */
56442     handleKeyDown : function(e){
56443         //Roo.log('Cell Sel Model handleKeyDown');
56444         if(!e.isNavKeyPress()){
56445             return;
56446         }
56447         var g = this.grid, s = this.selection;
56448         if(!s){
56449             e.stopEvent();
56450             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56451             if(cell){
56452                 this.select(cell[0], cell[1]);
56453             }
56454             return;
56455         }
56456         var sm = this;
56457         var walk = function(row, col, step){
56458             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56459         };
56460         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56461         var newCell;
56462
56463       
56464
56465         switch(k){
56466             case e.TAB:
56467                 // handled by onEditorKey
56468                 if (g.isEditor && g.editing) {
56469                     return;
56470                 }
56471                 if(e.shiftKey) {
56472                     newCell = walk(r, c-1, -1);
56473                 } else {
56474                     newCell = walk(r, c+1, 1);
56475                 }
56476                 break;
56477             
56478             case e.DOWN:
56479                newCell = walk(r+1, c, 1);
56480                 break;
56481             
56482             case e.UP:
56483                 newCell = walk(r-1, c, -1);
56484                 break;
56485             
56486             case e.RIGHT:
56487                 newCell = walk(r, c+1, 1);
56488                 break;
56489             
56490             case e.LEFT:
56491                 newCell = walk(r, c-1, -1);
56492                 break;
56493             
56494             case e.ENTER:
56495                 
56496                 if(g.isEditor && !g.editing){
56497                    g.startEditing(r, c);
56498                    e.stopEvent();
56499                    return;
56500                 }
56501                 
56502                 
56503              break;
56504         };
56505         if(newCell){
56506             this.select(newCell[0], newCell[1]);
56507             e.stopEvent();
56508             
56509         }
56510     },
56511
56512     acceptsNav : function(row, col, cm){
56513         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56514     },
56515     /**
56516      * Selects a cell.
56517      * @param {Number} field (not used) - as it's normally used as a listener
56518      * @param {Number} e - event - fake it by using
56519      *
56520      * var e = Roo.EventObjectImpl.prototype;
56521      * e.keyCode = e.TAB
56522      *
56523      * 
56524      */
56525     onEditorKey : function(field, e){
56526         
56527         var k = e.getKey(),
56528             newCell,
56529             g = this.grid,
56530             ed = g.activeEditor,
56531             forward = false;
56532         ///Roo.log('onEditorKey' + k);
56533         
56534         
56535         if (this.enter_is_tab && k == e.ENTER) {
56536             k = e.TAB;
56537         }
56538         
56539         if(k == e.TAB){
56540             if(e.shiftKey){
56541                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56542             }else{
56543                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56544                 forward = true;
56545             }
56546             
56547             e.stopEvent();
56548             
56549         } else if(k == e.ENTER &&  !e.ctrlKey){
56550             ed.completeEdit();
56551             e.stopEvent();
56552             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56553         
56554                 } else if(k == e.ESC){
56555             ed.cancelEdit();
56556         }
56557                 
56558         if (newCell) {
56559             var ecall = { cell : newCell, forward : forward };
56560             this.fireEvent('beforeeditnext', ecall );
56561             newCell = ecall.cell;
56562                         forward = ecall.forward;
56563         }
56564                 
56565         if(newCell){
56566             //Roo.log('next cell after edit');
56567             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56568         } else if (forward) {
56569             // tabbed past last
56570             this.fireEvent.defer(100, this, ['tabend',this]);
56571         }
56572     }
56573 });/*
56574  * Based on:
56575  * Ext JS Library 1.1.1
56576  * Copyright(c) 2006-2007, Ext JS, LLC.
56577  *
56578  * Originally Released Under LGPL - original licence link has changed is not relivant.
56579  *
56580  * Fork - LGPL
56581  * <script type="text/javascript">
56582  */
56583  
56584 /**
56585  * @class Roo.grid.EditorGrid
56586  * @extends Roo.grid.Grid
56587  * Class for creating and editable grid.
56588  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56589  * The container MUST have some type of size defined for the grid to fill. The container will be 
56590  * automatically set to position relative if it isn't already.
56591  * @param {Object} dataSource The data model to bind to
56592  * @param {Object} colModel The column model with info about this grid's columns
56593  */
56594 Roo.grid.EditorGrid = function(container, config){
56595     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56596     this.getGridEl().addClass("xedit-grid");
56597
56598     if(!this.selModel){
56599         this.selModel = new Roo.grid.CellSelectionModel();
56600     }
56601
56602     this.activeEditor = null;
56603
56604         this.addEvents({
56605             /**
56606              * @event beforeedit
56607              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56608              * <ul style="padding:5px;padding-left:16px;">
56609              * <li>grid - This grid</li>
56610              * <li>record - The record being edited</li>
56611              * <li>field - The field name being edited</li>
56612              * <li>value - The value for the field being edited.</li>
56613              * <li>row - The grid row index</li>
56614              * <li>column - The grid column index</li>
56615              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56616              * </ul>
56617              * @param {Object} e An edit event (see above for description)
56618              */
56619             "beforeedit" : true,
56620             /**
56621              * @event afteredit
56622              * Fires after a cell is edited. <br />
56623              * <ul style="padding:5px;padding-left:16px;">
56624              * <li>grid - This grid</li>
56625              * <li>record - The record being edited</li>
56626              * <li>field - The field name being edited</li>
56627              * <li>value - The value being set</li>
56628              * <li>originalValue - The original value for the field, before the edit.</li>
56629              * <li>row - The grid row index</li>
56630              * <li>column - The grid column index</li>
56631              * </ul>
56632              * @param {Object} e An edit event (see above for description)
56633              */
56634             "afteredit" : true,
56635             /**
56636              * @event validateedit
56637              * Fires after a cell is edited, but before the value is set in the record. 
56638          * You can use this to modify the value being set in the field, Return false
56639              * to cancel the change. The edit event object has the following properties <br />
56640              * <ul style="padding:5px;padding-left:16px;">
56641          * <li>editor - This editor</li>
56642              * <li>grid - This grid</li>
56643              * <li>record - The record being edited</li>
56644              * <li>field - The field name being edited</li>
56645              * <li>value - The value being set</li>
56646              * <li>originalValue - The original value for the field, before the edit.</li>
56647              * <li>row - The grid row index</li>
56648              * <li>column - The grid column index</li>
56649              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56650              * </ul>
56651              * @param {Object} e An edit event (see above for description)
56652              */
56653             "validateedit" : true
56654         });
56655     this.on("bodyscroll", this.stopEditing,  this);
56656     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56657 };
56658
56659 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56660     /**
56661      * @cfg {Number} clicksToEdit
56662      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56663      */
56664     clicksToEdit: 2,
56665
56666     // private
56667     isEditor : true,
56668     // private
56669     trackMouseOver: false, // causes very odd FF errors
56670
56671     onCellDblClick : function(g, row, col){
56672         this.startEditing(row, col);
56673     },
56674
56675     onEditComplete : function(ed, value, startValue){
56676         this.editing = false;
56677         this.activeEditor = null;
56678         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56679         var r = ed.record;
56680         var field = this.colModel.getDataIndex(ed.col);
56681         var e = {
56682             grid: this,
56683             record: r,
56684             field: field,
56685             originalValue: startValue,
56686             value: value,
56687             row: ed.row,
56688             column: ed.col,
56689             cancel:false,
56690             editor: ed
56691         };
56692         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56693         cell.show();
56694           
56695         if(String(value) !== String(startValue)){
56696             
56697             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56698                 r.set(field, e.value);
56699                 // if we are dealing with a combo box..
56700                 // then we also set the 'name' colum to be the displayField
56701                 if (ed.field.displayField && ed.field.name) {
56702                     r.set(ed.field.name, ed.field.el.dom.value);
56703                 }
56704                 
56705                 delete e.cancel; //?? why!!!
56706                 this.fireEvent("afteredit", e);
56707             }
56708         } else {
56709             this.fireEvent("afteredit", e); // always fire it!
56710         }
56711         this.view.focusCell(ed.row, ed.col);
56712     },
56713
56714     /**
56715      * Starts editing the specified for the specified row/column
56716      * @param {Number} rowIndex
56717      * @param {Number} colIndex
56718      */
56719     startEditing : function(row, col){
56720         this.stopEditing();
56721         if(this.colModel.isCellEditable(col, row)){
56722             this.view.ensureVisible(row, col, true);
56723           
56724             var r = this.dataSource.getAt(row);
56725             var field = this.colModel.getDataIndex(col);
56726             var cell = Roo.get(this.view.getCell(row,col));
56727             var e = {
56728                 grid: this,
56729                 record: r,
56730                 field: field,
56731                 value: r.data[field],
56732                 row: row,
56733                 column: col,
56734                 cancel:false 
56735             };
56736             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56737                 this.editing = true;
56738                 var ed = this.colModel.getCellEditor(col, row);
56739                 
56740                 if (!ed) {
56741                     return;
56742                 }
56743                 if(!ed.rendered){
56744                     ed.render(ed.parentEl || document.body);
56745                 }
56746                 ed.field.reset();
56747                
56748                 cell.hide();
56749                 
56750                 (function(){ // complex but required for focus issues in safari, ie and opera
56751                     ed.row = row;
56752                     ed.col = col;
56753                     ed.record = r;
56754                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56755                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56756                     this.activeEditor = ed;
56757                     var v = r.data[field];
56758                     ed.startEdit(this.view.getCell(row, col), v);
56759                     // combo's with 'displayField and name set
56760                     if (ed.field.displayField && ed.field.name) {
56761                         ed.field.el.dom.value = r.data[ed.field.name];
56762                     }
56763                     
56764                     
56765                 }).defer(50, this);
56766             }
56767         }
56768     },
56769         
56770     /**
56771      * Stops any active editing
56772      */
56773     stopEditing : function(){
56774         if(this.activeEditor){
56775             this.activeEditor.completeEdit();
56776         }
56777         this.activeEditor = null;
56778     },
56779         
56780          /**
56781      * Called to get grid's drag proxy text, by default returns this.ddText.
56782      * @return {String}
56783      */
56784     getDragDropText : function(){
56785         var count = this.selModel.getSelectedCell() ? 1 : 0;
56786         return String.format(this.ddText, count, count == 1 ? '' : 's');
56787     }
56788         
56789 });/*
56790  * Based on:
56791  * Ext JS Library 1.1.1
56792  * Copyright(c) 2006-2007, Ext JS, LLC.
56793  *
56794  * Originally Released Under LGPL - original licence link has changed is not relivant.
56795  *
56796  * Fork - LGPL
56797  * <script type="text/javascript">
56798  */
56799
56800 // private - not really -- you end up using it !
56801 // This is a support class used internally by the Grid components
56802
56803 /**
56804  * @class Roo.grid.GridEditor
56805  * @extends Roo.Editor
56806  * Class for creating and editable grid elements.
56807  * @param {Object} config any settings (must include field)
56808  */
56809 Roo.grid.GridEditor = function(field, config){
56810     if (!config && field.field) {
56811         config = field;
56812         field = Roo.factory(config.field, Roo.form);
56813     }
56814     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56815     field.monitorTab = false;
56816 };
56817
56818 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56819     
56820     /**
56821      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56822      */
56823     
56824     alignment: "tl-tl",
56825     autoSize: "width",
56826     hideEl : false,
56827     cls: "x-small-editor x-grid-editor",
56828     shim:false,
56829     shadow:"frame"
56830 });/*
56831  * Based on:
56832  * Ext JS Library 1.1.1
56833  * Copyright(c) 2006-2007, Ext JS, LLC.
56834  *
56835  * Originally Released Under LGPL - original licence link has changed is not relivant.
56836  *
56837  * Fork - LGPL
56838  * <script type="text/javascript">
56839  */
56840   
56841
56842   
56843 Roo.grid.PropertyRecord = Roo.data.Record.create([
56844     {name:'name',type:'string'},  'value'
56845 ]);
56846
56847
56848 Roo.grid.PropertyStore = function(grid, source){
56849     this.grid = grid;
56850     this.store = new Roo.data.Store({
56851         recordType : Roo.grid.PropertyRecord
56852     });
56853     this.store.on('update', this.onUpdate,  this);
56854     if(source){
56855         this.setSource(source);
56856     }
56857     Roo.grid.PropertyStore.superclass.constructor.call(this);
56858 };
56859
56860
56861
56862 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56863     setSource : function(o){
56864         this.source = o;
56865         this.store.removeAll();
56866         var data = [];
56867         for(var k in o){
56868             if(this.isEditableValue(o[k])){
56869                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56870             }
56871         }
56872         this.store.loadRecords({records: data}, {}, true);
56873     },
56874
56875     onUpdate : function(ds, record, type){
56876         if(type == Roo.data.Record.EDIT){
56877             var v = record.data['value'];
56878             var oldValue = record.modified['value'];
56879             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56880                 this.source[record.id] = v;
56881                 record.commit();
56882                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56883             }else{
56884                 record.reject();
56885             }
56886         }
56887     },
56888
56889     getProperty : function(row){
56890        return this.store.getAt(row);
56891     },
56892
56893     isEditableValue: function(val){
56894         if(val && val instanceof Date){
56895             return true;
56896         }else if(typeof val == 'object' || typeof val == 'function'){
56897             return false;
56898         }
56899         return true;
56900     },
56901
56902     setValue : function(prop, value){
56903         this.source[prop] = value;
56904         this.store.getById(prop).set('value', value);
56905     },
56906
56907     getSource : function(){
56908         return this.source;
56909     }
56910 });
56911
56912 Roo.grid.PropertyColumnModel = function(grid, store){
56913     this.grid = grid;
56914     var g = Roo.grid;
56915     g.PropertyColumnModel.superclass.constructor.call(this, [
56916         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56917         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56918     ]);
56919     this.store = store;
56920     this.bselect = Roo.DomHelper.append(document.body, {
56921         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56922             {tag: 'option', value: 'true', html: 'true'},
56923             {tag: 'option', value: 'false', html: 'false'}
56924         ]
56925     });
56926     Roo.id(this.bselect);
56927     var f = Roo.form;
56928     this.editors = {
56929         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56930         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56931         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56932         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56933         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56934     };
56935     this.renderCellDelegate = this.renderCell.createDelegate(this);
56936     this.renderPropDelegate = this.renderProp.createDelegate(this);
56937 };
56938
56939 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56940     
56941     
56942     nameText : 'Name',
56943     valueText : 'Value',
56944     
56945     dateFormat : 'm/j/Y',
56946     
56947     
56948     renderDate : function(dateVal){
56949         return dateVal.dateFormat(this.dateFormat);
56950     },
56951
56952     renderBool : function(bVal){
56953         return bVal ? 'true' : 'false';
56954     },
56955
56956     isCellEditable : function(colIndex, rowIndex){
56957         return colIndex == 1;
56958     },
56959
56960     getRenderer : function(col){
56961         return col == 1 ?
56962             this.renderCellDelegate : this.renderPropDelegate;
56963     },
56964
56965     renderProp : function(v){
56966         return this.getPropertyName(v);
56967     },
56968
56969     renderCell : function(val){
56970         var rv = val;
56971         if(val instanceof Date){
56972             rv = this.renderDate(val);
56973         }else if(typeof val == 'boolean'){
56974             rv = this.renderBool(val);
56975         }
56976         return Roo.util.Format.htmlEncode(rv);
56977     },
56978
56979     getPropertyName : function(name){
56980         var pn = this.grid.propertyNames;
56981         return pn && pn[name] ? pn[name] : name;
56982     },
56983
56984     getCellEditor : function(colIndex, rowIndex){
56985         var p = this.store.getProperty(rowIndex);
56986         var n = p.data['name'], val = p.data['value'];
56987         
56988         if(typeof(this.grid.customEditors[n]) == 'string'){
56989             return this.editors[this.grid.customEditors[n]];
56990         }
56991         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56992             return this.grid.customEditors[n];
56993         }
56994         if(val instanceof Date){
56995             return this.editors['date'];
56996         }else if(typeof val == 'number'){
56997             return this.editors['number'];
56998         }else if(typeof val == 'boolean'){
56999             return this.editors['boolean'];
57000         }else{
57001             return this.editors['string'];
57002         }
57003     }
57004 });
57005
57006 /**
57007  * @class Roo.grid.PropertyGrid
57008  * @extends Roo.grid.EditorGrid
57009  * This class represents the  interface of a component based property grid control.
57010  * <br><br>Usage:<pre><code>
57011  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57012       
57013  });
57014  // set any options
57015  grid.render();
57016  * </code></pre>
57017   
57018  * @constructor
57019  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57020  * The container MUST have some type of size defined for the grid to fill. The container will be
57021  * automatically set to position relative if it isn't already.
57022  * @param {Object} config A config object that sets properties on this grid.
57023  */
57024 Roo.grid.PropertyGrid = function(container, config){
57025     config = config || {};
57026     var store = new Roo.grid.PropertyStore(this);
57027     this.store = store;
57028     var cm = new Roo.grid.PropertyColumnModel(this, store);
57029     store.store.sort('name', 'ASC');
57030     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57031         ds: store.store,
57032         cm: cm,
57033         enableColLock:false,
57034         enableColumnMove:false,
57035         stripeRows:false,
57036         trackMouseOver: false,
57037         clicksToEdit:1
57038     }, config));
57039     this.getGridEl().addClass('x-props-grid');
57040     this.lastEditRow = null;
57041     this.on('columnresize', this.onColumnResize, this);
57042     this.addEvents({
57043          /**
57044              * @event beforepropertychange
57045              * Fires before a property changes (return false to stop?)
57046              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57047              * @param {String} id Record Id
57048              * @param {String} newval New Value
57049          * @param {String} oldval Old Value
57050              */
57051         "beforepropertychange": true,
57052         /**
57053              * @event propertychange
57054              * Fires after a property changes
57055              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57056              * @param {String} id Record Id
57057              * @param {String} newval New Value
57058          * @param {String} oldval Old Value
57059              */
57060         "propertychange": true
57061     });
57062     this.customEditors = this.customEditors || {};
57063 };
57064 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57065     
57066      /**
57067      * @cfg {Object} customEditors map of colnames=> custom editors.
57068      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57069      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57070      * false disables editing of the field.
57071          */
57072     
57073       /**
57074      * @cfg {Object} propertyNames map of property Names to their displayed value
57075          */
57076     
57077     render : function(){
57078         Roo.grid.PropertyGrid.superclass.render.call(this);
57079         this.autoSize.defer(100, this);
57080     },
57081
57082     autoSize : function(){
57083         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57084         if(this.view){
57085             this.view.fitColumns();
57086         }
57087     },
57088
57089     onColumnResize : function(){
57090         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57091         this.autoSize();
57092     },
57093     /**
57094      * Sets the data for the Grid
57095      * accepts a Key => Value object of all the elements avaiable.
57096      * @param {Object} data  to appear in grid.
57097      */
57098     setSource : function(source){
57099         this.store.setSource(source);
57100         //this.autoSize();
57101     },
57102     /**
57103      * Gets all the data from the grid.
57104      * @return {Object} data  data stored in grid
57105      */
57106     getSource : function(){
57107         return this.store.getSource();
57108     }
57109 });/*
57110   
57111  * Licence LGPL
57112  
57113  */
57114  
57115 /**
57116  * @class Roo.grid.Calendar
57117  * @extends Roo.util.Grid
57118  * This class extends the Grid to provide a calendar widget
57119  * <br><br>Usage:<pre><code>
57120  var grid = new Roo.grid.Calendar("my-container-id", {
57121      ds: myDataStore,
57122      cm: myColModel,
57123      selModel: mySelectionModel,
57124      autoSizeColumns: true,
57125      monitorWindowResize: false,
57126      trackMouseOver: true
57127      eventstore : real data store..
57128  });
57129  // set any options
57130  grid.render();
57131   
57132   * @constructor
57133  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57134  * The container MUST have some type of size defined for the grid to fill. The container will be
57135  * automatically set to position relative if it isn't already.
57136  * @param {Object} config A config object that sets properties on this grid.
57137  */
57138 Roo.grid.Calendar = function(container, config){
57139         // initialize the container
57140         this.container = Roo.get(container);
57141         this.container.update("");
57142         this.container.setStyle("overflow", "hidden");
57143     this.container.addClass('x-grid-container');
57144
57145     this.id = this.container.id;
57146
57147     Roo.apply(this, config);
57148     // check and correct shorthanded configs
57149     
57150     var rows = [];
57151     var d =1;
57152     for (var r = 0;r < 6;r++) {
57153         
57154         rows[r]=[];
57155         for (var c =0;c < 7;c++) {
57156             rows[r][c]= '';
57157         }
57158     }
57159     if (this.eventStore) {
57160         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57161         this.eventStore.on('load',this.onLoad, this);
57162         this.eventStore.on('beforeload',this.clearEvents, this);
57163          
57164     }
57165     
57166     this.dataSource = new Roo.data.Store({
57167             proxy: new Roo.data.MemoryProxy(rows),
57168             reader: new Roo.data.ArrayReader({}, [
57169                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57170     });
57171
57172     this.dataSource.load();
57173     this.ds = this.dataSource;
57174     this.ds.xmodule = this.xmodule || false;
57175     
57176     
57177     var cellRender = function(v,x,r)
57178     {
57179         return String.format(
57180             '<div class="fc-day  fc-widget-content"><div>' +
57181                 '<div class="fc-event-container"></div>' +
57182                 '<div class="fc-day-number">{0}</div>'+
57183                 
57184                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57185             '</div></div>', v);
57186     
57187     }
57188     
57189     
57190     this.colModel = new Roo.grid.ColumnModel( [
57191         {
57192             xtype: 'ColumnModel',
57193             xns: Roo.grid,
57194             dataIndex : 'weekday0',
57195             header : 'Sunday',
57196             renderer : cellRender
57197         },
57198         {
57199             xtype: 'ColumnModel',
57200             xns: Roo.grid,
57201             dataIndex : 'weekday1',
57202             header : 'Monday',
57203             renderer : cellRender
57204         },
57205         {
57206             xtype: 'ColumnModel',
57207             xns: Roo.grid,
57208             dataIndex : 'weekday2',
57209             header : 'Tuesday',
57210             renderer : cellRender
57211         },
57212         {
57213             xtype: 'ColumnModel',
57214             xns: Roo.grid,
57215             dataIndex : 'weekday3',
57216             header : 'Wednesday',
57217             renderer : cellRender
57218         },
57219         {
57220             xtype: 'ColumnModel',
57221             xns: Roo.grid,
57222             dataIndex : 'weekday4',
57223             header : 'Thursday',
57224             renderer : cellRender
57225         },
57226         {
57227             xtype: 'ColumnModel',
57228             xns: Roo.grid,
57229             dataIndex : 'weekday5',
57230             header : 'Friday',
57231             renderer : cellRender
57232         },
57233         {
57234             xtype: 'ColumnModel',
57235             xns: Roo.grid,
57236             dataIndex : 'weekday6',
57237             header : 'Saturday',
57238             renderer : cellRender
57239         }
57240     ]);
57241     this.cm = this.colModel;
57242     this.cm.xmodule = this.xmodule || false;
57243  
57244         
57245           
57246     //this.selModel = new Roo.grid.CellSelectionModel();
57247     //this.sm = this.selModel;
57248     //this.selModel.init(this);
57249     
57250     
57251     if(this.width){
57252         this.container.setWidth(this.width);
57253     }
57254
57255     if(this.height){
57256         this.container.setHeight(this.height);
57257     }
57258     /** @private */
57259         this.addEvents({
57260         // raw events
57261         /**
57262          * @event click
57263          * The raw click event for the entire grid.
57264          * @param {Roo.EventObject} e
57265          */
57266         "click" : true,
57267         /**
57268          * @event dblclick
57269          * The raw dblclick event for the entire grid.
57270          * @param {Roo.EventObject} e
57271          */
57272         "dblclick" : true,
57273         /**
57274          * @event contextmenu
57275          * The raw contextmenu event for the entire grid.
57276          * @param {Roo.EventObject} e
57277          */
57278         "contextmenu" : true,
57279         /**
57280          * @event mousedown
57281          * The raw mousedown event for the entire grid.
57282          * @param {Roo.EventObject} e
57283          */
57284         "mousedown" : true,
57285         /**
57286          * @event mouseup
57287          * The raw mouseup event for the entire grid.
57288          * @param {Roo.EventObject} e
57289          */
57290         "mouseup" : true,
57291         /**
57292          * @event mouseover
57293          * The raw mouseover event for the entire grid.
57294          * @param {Roo.EventObject} e
57295          */
57296         "mouseover" : true,
57297         /**
57298          * @event mouseout
57299          * The raw mouseout event for the entire grid.
57300          * @param {Roo.EventObject} e
57301          */
57302         "mouseout" : true,
57303         /**
57304          * @event keypress
57305          * The raw keypress event for the entire grid.
57306          * @param {Roo.EventObject} e
57307          */
57308         "keypress" : true,
57309         /**
57310          * @event keydown
57311          * The raw keydown event for the entire grid.
57312          * @param {Roo.EventObject} e
57313          */
57314         "keydown" : true,
57315
57316         // custom events
57317
57318         /**
57319          * @event cellclick
57320          * Fires when a cell is clicked
57321          * @param {Grid} this
57322          * @param {Number} rowIndex
57323          * @param {Number} columnIndex
57324          * @param {Roo.EventObject} e
57325          */
57326         "cellclick" : true,
57327         /**
57328          * @event celldblclick
57329          * Fires when a cell is double clicked
57330          * @param {Grid} this
57331          * @param {Number} rowIndex
57332          * @param {Number} columnIndex
57333          * @param {Roo.EventObject} e
57334          */
57335         "celldblclick" : true,
57336         /**
57337          * @event rowclick
57338          * Fires when a row is clicked
57339          * @param {Grid} this
57340          * @param {Number} rowIndex
57341          * @param {Roo.EventObject} e
57342          */
57343         "rowclick" : true,
57344         /**
57345          * @event rowdblclick
57346          * Fires when a row is double clicked
57347          * @param {Grid} this
57348          * @param {Number} rowIndex
57349          * @param {Roo.EventObject} e
57350          */
57351         "rowdblclick" : true,
57352         /**
57353          * @event headerclick
57354          * Fires when a header is clicked
57355          * @param {Grid} this
57356          * @param {Number} columnIndex
57357          * @param {Roo.EventObject} e
57358          */
57359         "headerclick" : true,
57360         /**
57361          * @event headerdblclick
57362          * Fires when a header cell is double clicked
57363          * @param {Grid} this
57364          * @param {Number} columnIndex
57365          * @param {Roo.EventObject} e
57366          */
57367         "headerdblclick" : true,
57368         /**
57369          * @event rowcontextmenu
57370          * Fires when a row is right clicked
57371          * @param {Grid} this
57372          * @param {Number} rowIndex
57373          * @param {Roo.EventObject} e
57374          */
57375         "rowcontextmenu" : true,
57376         /**
57377          * @event cellcontextmenu
57378          * Fires when a cell is right clicked
57379          * @param {Grid} this
57380          * @param {Number} rowIndex
57381          * @param {Number} cellIndex
57382          * @param {Roo.EventObject} e
57383          */
57384          "cellcontextmenu" : true,
57385         /**
57386          * @event headercontextmenu
57387          * Fires when a header is right clicked
57388          * @param {Grid} this
57389          * @param {Number} columnIndex
57390          * @param {Roo.EventObject} e
57391          */
57392         "headercontextmenu" : true,
57393         /**
57394          * @event bodyscroll
57395          * Fires when the body element is scrolled
57396          * @param {Number} scrollLeft
57397          * @param {Number} scrollTop
57398          */
57399         "bodyscroll" : true,
57400         /**
57401          * @event columnresize
57402          * Fires when the user resizes a column
57403          * @param {Number} columnIndex
57404          * @param {Number} newSize
57405          */
57406         "columnresize" : true,
57407         /**
57408          * @event columnmove
57409          * Fires when the user moves a column
57410          * @param {Number} oldIndex
57411          * @param {Number} newIndex
57412          */
57413         "columnmove" : true,
57414         /**
57415          * @event startdrag
57416          * Fires when row(s) start being dragged
57417          * @param {Grid} this
57418          * @param {Roo.GridDD} dd The drag drop object
57419          * @param {event} e The raw browser event
57420          */
57421         "startdrag" : true,
57422         /**
57423          * @event enddrag
57424          * Fires when a drag operation is complete
57425          * @param {Grid} this
57426          * @param {Roo.GridDD} dd The drag drop object
57427          * @param {event} e The raw browser event
57428          */
57429         "enddrag" : true,
57430         /**
57431          * @event dragdrop
57432          * Fires when dragged row(s) are dropped on a valid DD target
57433          * @param {Grid} this
57434          * @param {Roo.GridDD} dd The drag drop object
57435          * @param {String} targetId The target drag drop object
57436          * @param {event} e The raw browser event
57437          */
57438         "dragdrop" : true,
57439         /**
57440          * @event dragover
57441          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57442          * @param {Grid} this
57443          * @param {Roo.GridDD} dd The drag drop object
57444          * @param {String} targetId The target drag drop object
57445          * @param {event} e The raw browser event
57446          */
57447         "dragover" : true,
57448         /**
57449          * @event dragenter
57450          *  Fires when the dragged row(s) first cross another DD target while being dragged
57451          * @param {Grid} this
57452          * @param {Roo.GridDD} dd The drag drop object
57453          * @param {String} targetId The target drag drop object
57454          * @param {event} e The raw browser event
57455          */
57456         "dragenter" : true,
57457         /**
57458          * @event dragout
57459          * Fires when the dragged row(s) leave another DD target while being dragged
57460          * @param {Grid} this
57461          * @param {Roo.GridDD} dd The drag drop object
57462          * @param {String} targetId The target drag drop object
57463          * @param {event} e The raw browser event
57464          */
57465         "dragout" : true,
57466         /**
57467          * @event rowclass
57468          * Fires when a row is rendered, so you can change add a style to it.
57469          * @param {GridView} gridview   The grid view
57470          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57471          */
57472         'rowclass' : true,
57473
57474         /**
57475          * @event render
57476          * Fires when the grid is rendered
57477          * @param {Grid} grid
57478          */
57479         'render' : true,
57480             /**
57481              * @event select
57482              * Fires when a date is selected
57483              * @param {DatePicker} this
57484              * @param {Date} date The selected date
57485              */
57486         'select': true,
57487         /**
57488              * @event monthchange
57489              * Fires when the displayed month changes 
57490              * @param {DatePicker} this
57491              * @param {Date} date The selected month
57492              */
57493         'monthchange': true,
57494         /**
57495              * @event evententer
57496              * Fires when mouse over an event
57497              * @param {Calendar} this
57498              * @param {event} Event
57499              */
57500         'evententer': true,
57501         /**
57502              * @event eventleave
57503              * Fires when the mouse leaves an
57504              * @param {Calendar} this
57505              * @param {event}
57506              */
57507         'eventleave': true,
57508         /**
57509              * @event eventclick
57510              * Fires when the mouse click an
57511              * @param {Calendar} this
57512              * @param {event}
57513              */
57514         'eventclick': true,
57515         /**
57516              * @event eventrender
57517              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57518              * @param {Calendar} this
57519              * @param {data} data to be modified
57520              */
57521         'eventrender': true
57522         
57523     });
57524
57525     Roo.grid.Grid.superclass.constructor.call(this);
57526     this.on('render', function() {
57527         this.view.el.addClass('x-grid-cal'); 
57528         
57529         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57530
57531     },this);
57532     
57533     if (!Roo.grid.Calendar.style) {
57534         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57535             
57536             
57537             '.x-grid-cal .x-grid-col' :  {
57538                 height: 'auto !important',
57539                 'vertical-align': 'top'
57540             },
57541             '.x-grid-cal  .fc-event-hori' : {
57542                 height: '14px'
57543             }
57544              
57545             
57546         }, Roo.id());
57547     }
57548
57549     
57550     
57551 };
57552 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57553     /**
57554      * @cfg {Store} eventStore The store that loads events.
57555      */
57556     eventStore : 25,
57557
57558      
57559     activeDate : false,
57560     startDay : 0,
57561     autoWidth : true,
57562     monitorWindowResize : false,
57563
57564     
57565     resizeColumns : function() {
57566         var col = (this.view.el.getWidth() / 7) - 3;
57567         // loop through cols, and setWidth
57568         for(var i =0 ; i < 7 ; i++){
57569             this.cm.setColumnWidth(i, col);
57570         }
57571     },
57572      setDate :function(date) {
57573         
57574         Roo.log('setDate?');
57575         
57576         this.resizeColumns();
57577         var vd = this.activeDate;
57578         this.activeDate = date;
57579 //        if(vd && this.el){
57580 //            var t = date.getTime();
57581 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57582 //                Roo.log('using add remove');
57583 //                
57584 //                this.fireEvent('monthchange', this, date);
57585 //                
57586 //                this.cells.removeClass("fc-state-highlight");
57587 //                this.cells.each(function(c){
57588 //                   if(c.dateValue == t){
57589 //                       c.addClass("fc-state-highlight");
57590 //                       setTimeout(function(){
57591 //                            try{c.dom.firstChild.focus();}catch(e){}
57592 //                       }, 50);
57593 //                       return false;
57594 //                   }
57595 //                   return true;
57596 //                });
57597 //                return;
57598 //            }
57599 //        }
57600         
57601         var days = date.getDaysInMonth();
57602         
57603         var firstOfMonth = date.getFirstDateOfMonth();
57604         var startingPos = firstOfMonth.getDay()-this.startDay;
57605         
57606         if(startingPos < this.startDay){
57607             startingPos += 7;
57608         }
57609         
57610         var pm = date.add(Date.MONTH, -1);
57611         var prevStart = pm.getDaysInMonth()-startingPos;
57612 //        
57613         
57614         
57615         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57616         
57617         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57618         //this.cells.addClassOnOver('fc-state-hover');
57619         
57620         var cells = this.cells.elements;
57621         var textEls = this.textNodes;
57622         
57623         //Roo.each(cells, function(cell){
57624         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57625         //});
57626         
57627         days += startingPos;
57628
57629         // convert everything to numbers so it's fast
57630         var day = 86400000;
57631         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57632         //Roo.log(d);
57633         //Roo.log(pm);
57634         //Roo.log(prevStart);
57635         
57636         var today = new Date().clearTime().getTime();
57637         var sel = date.clearTime().getTime();
57638         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57639         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57640         var ddMatch = this.disabledDatesRE;
57641         var ddText = this.disabledDatesText;
57642         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57643         var ddaysText = this.disabledDaysText;
57644         var format = this.format;
57645         
57646         var setCellClass = function(cal, cell){
57647             
57648             //Roo.log('set Cell Class');
57649             cell.title = "";
57650             var t = d.getTime();
57651             
57652             //Roo.log(d);
57653             
57654             
57655             cell.dateValue = t;
57656             if(t == today){
57657                 cell.className += " fc-today";
57658                 cell.className += " fc-state-highlight";
57659                 cell.title = cal.todayText;
57660             }
57661             if(t == sel){
57662                 // disable highlight in other month..
57663                 cell.className += " fc-state-highlight";
57664                 
57665             }
57666             // disabling
57667             if(t < min) {
57668                 //cell.className = " fc-state-disabled";
57669                 cell.title = cal.minText;
57670                 return;
57671             }
57672             if(t > max) {
57673                 //cell.className = " fc-state-disabled";
57674                 cell.title = cal.maxText;
57675                 return;
57676             }
57677             if(ddays){
57678                 if(ddays.indexOf(d.getDay()) != -1){
57679                     // cell.title = ddaysText;
57680                    // cell.className = " fc-state-disabled";
57681                 }
57682             }
57683             if(ddMatch && format){
57684                 var fvalue = d.dateFormat(format);
57685                 if(ddMatch.test(fvalue)){
57686                     cell.title = ddText.replace("%0", fvalue);
57687                    cell.className = " fc-state-disabled";
57688                 }
57689             }
57690             
57691             if (!cell.initialClassName) {
57692                 cell.initialClassName = cell.dom.className;
57693             }
57694             
57695             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57696         };
57697
57698         var i = 0;
57699         
57700         for(; i < startingPos; i++) {
57701             cells[i].dayName =  (++prevStart);
57702             Roo.log(textEls[i]);
57703             d.setDate(d.getDate()+1);
57704             
57705             //cells[i].className = "fc-past fc-other-month";
57706             setCellClass(this, cells[i]);
57707         }
57708         
57709         var intDay = 0;
57710         
57711         for(; i < days; i++){
57712             intDay = i - startingPos + 1;
57713             cells[i].dayName =  (intDay);
57714             d.setDate(d.getDate()+1);
57715             
57716             cells[i].className = ''; // "x-date-active";
57717             setCellClass(this, cells[i]);
57718         }
57719         var extraDays = 0;
57720         
57721         for(; i < 42; i++) {
57722             //textEls[i].innerHTML = (++extraDays);
57723             
57724             d.setDate(d.getDate()+1);
57725             cells[i].dayName = (++extraDays);
57726             cells[i].className = "fc-future fc-other-month";
57727             setCellClass(this, cells[i]);
57728         }
57729         
57730         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57731         
57732         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57733         
57734         // this will cause all the cells to mis
57735         var rows= [];
57736         var i =0;
57737         for (var r = 0;r < 6;r++) {
57738             for (var c =0;c < 7;c++) {
57739                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57740             }    
57741         }
57742         
57743         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57744         for(i=0;i<cells.length;i++) {
57745             
57746             this.cells.elements[i].dayName = cells[i].dayName ;
57747             this.cells.elements[i].className = cells[i].className;
57748             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57749             this.cells.elements[i].title = cells[i].title ;
57750             this.cells.elements[i].dateValue = cells[i].dateValue ;
57751         }
57752         
57753         
57754         
57755         
57756         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57757         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57758         
57759         ////if(totalRows != 6){
57760             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57761            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57762        // }
57763         
57764         this.fireEvent('monthchange', this, date);
57765         
57766         
57767     },
57768  /**
57769      * Returns the grid's SelectionModel.
57770      * @return {SelectionModel}
57771      */
57772     getSelectionModel : function(){
57773         if(!this.selModel){
57774             this.selModel = new Roo.grid.CellSelectionModel();
57775         }
57776         return this.selModel;
57777     },
57778
57779     load: function() {
57780         this.eventStore.load()
57781         
57782         
57783         
57784     },
57785     
57786     findCell : function(dt) {
57787         dt = dt.clearTime().getTime();
57788         var ret = false;
57789         this.cells.each(function(c){
57790             //Roo.log("check " +c.dateValue + '?=' + dt);
57791             if(c.dateValue == dt){
57792                 ret = c;
57793                 return false;
57794             }
57795             return true;
57796         });
57797         
57798         return ret;
57799     },
57800     
57801     findCells : function(rec) {
57802         var s = rec.data.start_dt.clone().clearTime().getTime();
57803        // Roo.log(s);
57804         var e= rec.data.end_dt.clone().clearTime().getTime();
57805        // Roo.log(e);
57806         var ret = [];
57807         this.cells.each(function(c){
57808              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57809             
57810             if(c.dateValue > e){
57811                 return ;
57812             }
57813             if(c.dateValue < s){
57814                 return ;
57815             }
57816             ret.push(c);
57817         });
57818         
57819         return ret;    
57820     },
57821     
57822     findBestRow: function(cells)
57823     {
57824         var ret = 0;
57825         
57826         for (var i =0 ; i < cells.length;i++) {
57827             ret  = Math.max(cells[i].rows || 0,ret);
57828         }
57829         return ret;
57830         
57831     },
57832     
57833     
57834     addItem : function(rec)
57835     {
57836         // look for vertical location slot in
57837         var cells = this.findCells(rec);
57838         
57839         rec.row = this.findBestRow(cells);
57840         
57841         // work out the location.
57842         
57843         var crow = false;
57844         var rows = [];
57845         for(var i =0; i < cells.length; i++) {
57846             if (!crow) {
57847                 crow = {
57848                     start : cells[i],
57849                     end :  cells[i]
57850                 };
57851                 continue;
57852             }
57853             if (crow.start.getY() == cells[i].getY()) {
57854                 // on same row.
57855                 crow.end = cells[i];
57856                 continue;
57857             }
57858             // different row.
57859             rows.push(crow);
57860             crow = {
57861                 start: cells[i],
57862                 end : cells[i]
57863             };
57864             
57865         }
57866         
57867         rows.push(crow);
57868         rec.els = [];
57869         rec.rows = rows;
57870         rec.cells = cells;
57871         for (var i = 0; i < cells.length;i++) {
57872             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57873             
57874         }
57875         
57876         
57877     },
57878     
57879     clearEvents: function() {
57880         
57881         if (!this.eventStore.getCount()) {
57882             return;
57883         }
57884         // reset number of rows in cells.
57885         Roo.each(this.cells.elements, function(c){
57886             c.rows = 0;
57887         });
57888         
57889         this.eventStore.each(function(e) {
57890             this.clearEvent(e);
57891         },this);
57892         
57893     },
57894     
57895     clearEvent : function(ev)
57896     {
57897         if (ev.els) {
57898             Roo.each(ev.els, function(el) {
57899                 el.un('mouseenter' ,this.onEventEnter, this);
57900                 el.un('mouseleave' ,this.onEventLeave, this);
57901                 el.remove();
57902             },this);
57903             ev.els = [];
57904         }
57905     },
57906     
57907     
57908     renderEvent : function(ev,ctr) {
57909         if (!ctr) {
57910              ctr = this.view.el.select('.fc-event-container',true).first();
57911         }
57912         
57913          
57914         this.clearEvent(ev);
57915             //code
57916        
57917         
57918         
57919         ev.els = [];
57920         var cells = ev.cells;
57921         var rows = ev.rows;
57922         this.fireEvent('eventrender', this, ev);
57923         
57924         for(var i =0; i < rows.length; i++) {
57925             
57926             cls = '';
57927             if (i == 0) {
57928                 cls += ' fc-event-start';
57929             }
57930             if ((i+1) == rows.length) {
57931                 cls += ' fc-event-end';
57932             }
57933             
57934             //Roo.log(ev.data);
57935             // how many rows should it span..
57936             var cg = this.eventTmpl.append(ctr,Roo.apply({
57937                 fccls : cls
57938                 
57939             }, ev.data) , true);
57940             
57941             
57942             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57943             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57944             cg.on('click', this.onEventClick, this, ev);
57945             
57946             ev.els.push(cg);
57947             
57948             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57949             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57950             //Roo.log(cg);
57951              
57952             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57953             cg.setWidth(ebox.right - sbox.x -2);
57954         }
57955     },
57956     
57957     renderEvents: function()
57958     {   
57959         // first make sure there is enough space..
57960         
57961         if (!this.eventTmpl) {
57962             this.eventTmpl = new Roo.Template(
57963                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57964                     '<div class="fc-event-inner">' +
57965                         '<span class="fc-event-time">{time}</span>' +
57966                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57967                     '</div>' +
57968                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57969                 '</div>'
57970             );
57971                 
57972         }
57973                
57974         
57975         
57976         this.cells.each(function(c) {
57977             //Roo.log(c.select('.fc-day-content div',true).first());
57978             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57979         });
57980         
57981         var ctr = this.view.el.select('.fc-event-container',true).first();
57982         
57983         var cls;
57984         this.eventStore.each(function(ev){
57985             
57986             this.renderEvent(ev);
57987              
57988              
57989         }, this);
57990         this.view.layout();
57991         
57992     },
57993     
57994     onEventEnter: function (e, el,event,d) {
57995         this.fireEvent('evententer', this, el, event);
57996     },
57997     
57998     onEventLeave: function (e, el,event,d) {
57999         this.fireEvent('eventleave', this, el, event);
58000     },
58001     
58002     onEventClick: function (e, el,event,d) {
58003         this.fireEvent('eventclick', this, el, event);
58004     },
58005     
58006     onMonthChange: function () {
58007         this.store.load();
58008     },
58009     
58010     onLoad: function () {
58011         
58012         //Roo.log('calendar onload');
58013 //         
58014         if(this.eventStore.getCount() > 0){
58015             
58016            
58017             
58018             this.eventStore.each(function(d){
58019                 
58020                 
58021                 // FIXME..
58022                 var add =   d.data;
58023                 if (typeof(add.end_dt) == 'undefined')  {
58024                     Roo.log("Missing End time in calendar data: ");
58025                     Roo.log(d);
58026                     return;
58027                 }
58028                 if (typeof(add.start_dt) == 'undefined')  {
58029                     Roo.log("Missing Start time in calendar data: ");
58030                     Roo.log(d);
58031                     return;
58032                 }
58033                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58034                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58035                 add.id = add.id || d.id;
58036                 add.title = add.title || '??';
58037                 
58038                 this.addItem(d);
58039                 
58040              
58041             },this);
58042         }
58043         
58044         this.renderEvents();
58045     }
58046     
58047
58048 });
58049 /*
58050  grid : {
58051                 xtype: 'Grid',
58052                 xns: Roo.grid,
58053                 listeners : {
58054                     render : function ()
58055                     {
58056                         _this.grid = this;
58057                         
58058                         if (!this.view.el.hasClass('course-timesheet')) {
58059                             this.view.el.addClass('course-timesheet');
58060                         }
58061                         if (this.tsStyle) {
58062                             this.ds.load({});
58063                             return; 
58064                         }
58065                         Roo.log('width');
58066                         Roo.log(_this.grid.view.el.getWidth());
58067                         
58068                         
58069                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58070                             '.course-timesheet .x-grid-row' : {
58071                                 height: '80px'
58072                             },
58073                             '.x-grid-row td' : {
58074                                 'vertical-align' : 0
58075                             },
58076                             '.course-edit-link' : {
58077                                 'color' : 'blue',
58078                                 'text-overflow' : 'ellipsis',
58079                                 'overflow' : 'hidden',
58080                                 'white-space' : 'nowrap',
58081                                 'cursor' : 'pointer'
58082                             },
58083                             '.sub-link' : {
58084                                 'color' : 'green'
58085                             },
58086                             '.de-act-sup-link' : {
58087                                 'color' : 'purple',
58088                                 'text-decoration' : 'line-through'
58089                             },
58090                             '.de-act-link' : {
58091                                 'color' : 'red',
58092                                 'text-decoration' : 'line-through'
58093                             },
58094                             '.course-timesheet .course-highlight' : {
58095                                 'border-top-style': 'dashed !important',
58096                                 'border-bottom-bottom': 'dashed !important'
58097                             },
58098                             '.course-timesheet .course-item' : {
58099                                 'font-family'   : 'tahoma, arial, helvetica',
58100                                 'font-size'     : '11px',
58101                                 'overflow'      : 'hidden',
58102                                 'padding-left'  : '10px',
58103                                 'padding-right' : '10px',
58104                                 'padding-top' : '10px' 
58105                             }
58106                             
58107                         }, Roo.id());
58108                                 this.ds.load({});
58109                     }
58110                 },
58111                 autoWidth : true,
58112                 monitorWindowResize : false,
58113                 cellrenderer : function(v,x,r)
58114                 {
58115                     return v;
58116                 },
58117                 sm : {
58118                     xtype: 'CellSelectionModel',
58119                     xns: Roo.grid
58120                 },
58121                 dataSource : {
58122                     xtype: 'Store',
58123                     xns: Roo.data,
58124                     listeners : {
58125                         beforeload : function (_self, options)
58126                         {
58127                             options.params = options.params || {};
58128                             options.params._month = _this.monthField.getValue();
58129                             options.params.limit = 9999;
58130                             options.params['sort'] = 'when_dt';    
58131                             options.params['dir'] = 'ASC';    
58132                             this.proxy.loadResponse = this.loadResponse;
58133                             Roo.log("load?");
58134                             //this.addColumns();
58135                         },
58136                         load : function (_self, records, options)
58137                         {
58138                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58139                                 // if you click on the translation.. you can edit it...
58140                                 var el = Roo.get(this);
58141                                 var id = el.dom.getAttribute('data-id');
58142                                 var d = el.dom.getAttribute('data-date');
58143                                 var t = el.dom.getAttribute('data-time');
58144                                 //var id = this.child('span').dom.textContent;
58145                                 
58146                                 //Roo.log(this);
58147                                 Pman.Dialog.CourseCalendar.show({
58148                                     id : id,
58149                                     when_d : d,
58150                                     when_t : t,
58151                                     productitem_active : id ? 1 : 0
58152                                 }, function() {
58153                                     _this.grid.ds.load({});
58154                                 });
58155                            
58156                            });
58157                            
58158                            _this.panel.fireEvent('resize', [ '', '' ]);
58159                         }
58160                     },
58161                     loadResponse : function(o, success, response){
58162                             // this is overridden on before load..
58163                             
58164                             Roo.log("our code?");       
58165                             //Roo.log(success);
58166                             //Roo.log(response)
58167                             delete this.activeRequest;
58168                             if(!success){
58169                                 this.fireEvent("loadexception", this, o, response);
58170                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58171                                 return;
58172                             }
58173                             var result;
58174                             try {
58175                                 result = o.reader.read(response);
58176                             }catch(e){
58177                                 Roo.log("load exception?");
58178                                 this.fireEvent("loadexception", this, o, response, e);
58179                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58180                                 return;
58181                             }
58182                             Roo.log("ready...");        
58183                             // loop through result.records;
58184                             // and set this.tdate[date] = [] << array of records..
58185                             _this.tdata  = {};
58186                             Roo.each(result.records, function(r){
58187                                 //Roo.log(r.data);
58188                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58189                                     _this.tdata[r.data.when_dt.format('j')] = [];
58190                                 }
58191                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58192                             });
58193                             
58194                             //Roo.log(_this.tdata);
58195                             
58196                             result.records = [];
58197                             result.totalRecords = 6;
58198                     
58199                             // let's generate some duumy records for the rows.
58200                             //var st = _this.dateField.getValue();
58201                             
58202                             // work out monday..
58203                             //st = st.add(Date.DAY, -1 * st.format('w'));
58204                             
58205                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58206                             
58207                             var firstOfMonth = date.getFirstDayOfMonth();
58208                             var days = date.getDaysInMonth();
58209                             var d = 1;
58210                             var firstAdded = false;
58211                             for (var i = 0; i < result.totalRecords ; i++) {
58212                                 //var d= st.add(Date.DAY, i);
58213                                 var row = {};
58214                                 var added = 0;
58215                                 for(var w = 0 ; w < 7 ; w++){
58216                                     if(!firstAdded && firstOfMonth != w){
58217                                         continue;
58218                                     }
58219                                     if(d > days){
58220                                         continue;
58221                                     }
58222                                     firstAdded = true;
58223                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58224                                     row['weekday'+w] = String.format(
58225                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58226                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58227                                                     d,
58228                                                     date.format('Y-m-')+dd
58229                                                 );
58230                                     added++;
58231                                     if(typeof(_this.tdata[d]) != 'undefined'){
58232                                         Roo.each(_this.tdata[d], function(r){
58233                                             var is_sub = '';
58234                                             var deactive = '';
58235                                             var id = r.id;
58236                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58237                                             if(r.parent_id*1>0){
58238                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58239                                                 id = r.parent_id;
58240                                             }
58241                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58242                                                 deactive = 'de-act-link';
58243                                             }
58244                                             
58245                                             row['weekday'+w] += String.format(
58246                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58247                                                     id, //0
58248                                                     r.product_id_name, //1
58249                                                     r.when_dt.format('h:ia'), //2
58250                                                     is_sub, //3
58251                                                     deactive, //4
58252                                                     desc // 5
58253                                             );
58254                                         });
58255                                     }
58256                                     d++;
58257                                 }
58258                                 
58259                                 // only do this if something added..
58260                                 if(added > 0){ 
58261                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58262                                 }
58263                                 
58264                                 
58265                                 // push it twice. (second one with an hour..
58266                                 
58267                             }
58268                             //Roo.log(result);
58269                             this.fireEvent("load", this, o, o.request.arg);
58270                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58271                         },
58272                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58273                     proxy : {
58274                         xtype: 'HttpProxy',
58275                         xns: Roo.data,
58276                         method : 'GET',
58277                         url : baseURL + '/Roo/Shop_course.php'
58278                     },
58279                     reader : {
58280                         xtype: 'JsonReader',
58281                         xns: Roo.data,
58282                         id : 'id',
58283                         fields : [
58284                             {
58285                                 'name': 'id',
58286                                 'type': 'int'
58287                             },
58288                             {
58289                                 'name': 'when_dt',
58290                                 'type': 'string'
58291                             },
58292                             {
58293                                 'name': 'end_dt',
58294                                 'type': 'string'
58295                             },
58296                             {
58297                                 'name': 'parent_id',
58298                                 'type': 'int'
58299                             },
58300                             {
58301                                 'name': 'product_id',
58302                                 'type': 'int'
58303                             },
58304                             {
58305                                 'name': 'productitem_id',
58306                                 'type': 'int'
58307                             },
58308                             {
58309                                 'name': 'guid',
58310                                 'type': 'int'
58311                             }
58312                         ]
58313                     }
58314                 },
58315                 toolbar : {
58316                     xtype: 'Toolbar',
58317                     xns: Roo,
58318                     items : [
58319                         {
58320                             xtype: 'Button',
58321                             xns: Roo.Toolbar,
58322                             listeners : {
58323                                 click : function (_self, e)
58324                                 {
58325                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58326                                     sd.setMonth(sd.getMonth()-1);
58327                                     _this.monthField.setValue(sd.format('Y-m-d'));
58328                                     _this.grid.ds.load({});
58329                                 }
58330                             },
58331                             text : "Back"
58332                         },
58333                         {
58334                             xtype: 'Separator',
58335                             xns: Roo.Toolbar
58336                         },
58337                         {
58338                             xtype: 'MonthField',
58339                             xns: Roo.form,
58340                             listeners : {
58341                                 render : function (_self)
58342                                 {
58343                                     _this.monthField = _self;
58344                                    // _this.monthField.set  today
58345                                 },
58346                                 select : function (combo, date)
58347                                 {
58348                                     _this.grid.ds.load({});
58349                                 }
58350                             },
58351                             value : (function() { return new Date(); })()
58352                         },
58353                         {
58354                             xtype: 'Separator',
58355                             xns: Roo.Toolbar
58356                         },
58357                         {
58358                             xtype: 'TextItem',
58359                             xns: Roo.Toolbar,
58360                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58361                         },
58362                         {
58363                             xtype: 'Fill',
58364                             xns: Roo.Toolbar
58365                         },
58366                         {
58367                             xtype: 'Button',
58368                             xns: Roo.Toolbar,
58369                             listeners : {
58370                                 click : function (_self, e)
58371                                 {
58372                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58373                                     sd.setMonth(sd.getMonth()+1);
58374                                     _this.monthField.setValue(sd.format('Y-m-d'));
58375                                     _this.grid.ds.load({});
58376                                 }
58377                             },
58378                             text : "Next"
58379                         }
58380                     ]
58381                 },
58382                  
58383             }
58384         };
58385         
58386         *//*
58387  * Based on:
58388  * Ext JS Library 1.1.1
58389  * Copyright(c) 2006-2007, Ext JS, LLC.
58390  *
58391  * Originally Released Under LGPL - original licence link has changed is not relivant.
58392  *
58393  * Fork - LGPL
58394  * <script type="text/javascript">
58395  */
58396  
58397 /**
58398  * @class Roo.LoadMask
58399  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58400  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58401  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58402  * element's UpdateManager load indicator and will be destroyed after the initial load.
58403  * @constructor
58404  * Create a new LoadMask
58405  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58406  * @param {Object} config The config object
58407  */
58408 Roo.LoadMask = function(el, config){
58409     this.el = Roo.get(el);
58410     Roo.apply(this, config);
58411     if(this.store){
58412         this.store.on('beforeload', this.onBeforeLoad, this);
58413         this.store.on('load', this.onLoad, this);
58414         this.store.on('loadexception', this.onLoadException, this);
58415         this.removeMask = false;
58416     }else{
58417         var um = this.el.getUpdateManager();
58418         um.showLoadIndicator = false; // disable the default indicator
58419         um.on('beforeupdate', this.onBeforeLoad, this);
58420         um.on('update', this.onLoad, this);
58421         um.on('failure', this.onLoad, this);
58422         this.removeMask = true;
58423     }
58424 };
58425
58426 Roo.LoadMask.prototype = {
58427     /**
58428      * @cfg {Boolean} removeMask
58429      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58430      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58431      */
58432     /**
58433      * @cfg {String} msg
58434      * The text to display in a centered loading message box (defaults to 'Loading...')
58435      */
58436     msg : 'Loading...',
58437     /**
58438      * @cfg {String} msgCls
58439      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58440      */
58441     msgCls : 'x-mask-loading',
58442
58443     /**
58444      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58445      * @type Boolean
58446      */
58447     disabled: false,
58448
58449     /**
58450      * Disables the mask to prevent it from being displayed
58451      */
58452     disable : function(){
58453        this.disabled = true;
58454     },
58455
58456     /**
58457      * Enables the mask so that it can be displayed
58458      */
58459     enable : function(){
58460         this.disabled = false;
58461     },
58462     
58463     onLoadException : function()
58464     {
58465         Roo.log(arguments);
58466         
58467         if (typeof(arguments[3]) != 'undefined') {
58468             Roo.MessageBox.alert("Error loading",arguments[3]);
58469         } 
58470         /*
58471         try {
58472             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58473                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58474             }   
58475         } catch(e) {
58476             
58477         }
58478         */
58479     
58480         
58481         
58482         this.el.unmask(this.removeMask);
58483     },
58484     // private
58485     onLoad : function()
58486     {
58487         this.el.unmask(this.removeMask);
58488     },
58489
58490     // private
58491     onBeforeLoad : function(){
58492         if(!this.disabled){
58493             this.el.mask(this.msg, this.msgCls);
58494         }
58495     },
58496
58497     // private
58498     destroy : function(){
58499         if(this.store){
58500             this.store.un('beforeload', this.onBeforeLoad, this);
58501             this.store.un('load', this.onLoad, this);
58502             this.store.un('loadexception', this.onLoadException, this);
58503         }else{
58504             var um = this.el.getUpdateManager();
58505             um.un('beforeupdate', this.onBeforeLoad, this);
58506             um.un('update', this.onLoad, this);
58507             um.un('failure', this.onLoad, this);
58508         }
58509     }
58510 };/*
58511  * Based on:
58512  * Ext JS Library 1.1.1
58513  * Copyright(c) 2006-2007, Ext JS, LLC.
58514  *
58515  * Originally Released Under LGPL - original licence link has changed is not relivant.
58516  *
58517  * Fork - LGPL
58518  * <script type="text/javascript">
58519  */
58520
58521
58522 /**
58523  * @class Roo.XTemplate
58524  * @extends Roo.Template
58525  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58526 <pre><code>
58527 var t = new Roo.XTemplate(
58528         '&lt;select name="{name}"&gt;',
58529                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58530         '&lt;/select&gt;'
58531 );
58532  
58533 // then append, applying the master template values
58534  </code></pre>
58535  *
58536  * Supported features:
58537  *
58538  *  Tags:
58539
58540 <pre><code>
58541       {a_variable} - output encoded.
58542       {a_variable.format:("Y-m-d")} - call a method on the variable
58543       {a_variable:raw} - unencoded output
58544       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58545       {a_variable:this.method_on_template(...)} - call a method on the template object.
58546  
58547 </code></pre>
58548  *  The tpl tag:
58549 <pre><code>
58550         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58551         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58552         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58553         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58554   
58555         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58556         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58557 </code></pre>
58558  *      
58559  */
58560 Roo.XTemplate = function()
58561 {
58562     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58563     if (this.html) {
58564         this.compile();
58565     }
58566 };
58567
58568
58569 Roo.extend(Roo.XTemplate, Roo.Template, {
58570
58571     /**
58572      * The various sub templates
58573      */
58574     tpls : false,
58575     /**
58576      *
58577      * basic tag replacing syntax
58578      * WORD:WORD()
58579      *
58580      * // you can fake an object call by doing this
58581      *  x.t:(test,tesT) 
58582      * 
58583      */
58584     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58585
58586     /**
58587      * compile the template
58588      *
58589      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58590      *
58591      */
58592     compile: function()
58593     {
58594         var s = this.html;
58595      
58596         s = ['<tpl>', s, '</tpl>'].join('');
58597     
58598         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58599             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58600             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58601             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58602             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58603             m,
58604             id     = 0,
58605             tpls   = [];
58606     
58607         while(true == !!(m = s.match(re))){
58608             var forMatch   = m[0].match(nameRe),
58609                 ifMatch   = m[0].match(ifRe),
58610                 execMatch   = m[0].match(execRe),
58611                 namedMatch   = m[0].match(namedRe),
58612                 
58613                 exp  = null, 
58614                 fn   = null,
58615                 exec = null,
58616                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58617                 
58618             if (ifMatch) {
58619                 // if - puts fn into test..
58620                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58621                 if(exp){
58622                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58623                 }
58624             }
58625             
58626             if (execMatch) {
58627                 // exec - calls a function... returns empty if true is  returned.
58628                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58629                 if(exp){
58630                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58631                 }
58632             }
58633             
58634             
58635             if (name) {
58636                 // for = 
58637                 switch(name){
58638                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58639                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58640                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58641                 }
58642             }
58643             var uid = namedMatch ? namedMatch[1] : id;
58644             
58645             
58646             tpls.push({
58647                 id:     namedMatch ? namedMatch[1] : id,
58648                 target: name,
58649                 exec:   exec,
58650                 test:   fn,
58651                 body:   m[1] || ''
58652             });
58653             if (namedMatch) {
58654                 s = s.replace(m[0], '');
58655             } else { 
58656                 s = s.replace(m[0], '{xtpl'+ id + '}');
58657             }
58658             ++id;
58659         }
58660         this.tpls = [];
58661         for(var i = tpls.length-1; i >= 0; --i){
58662             this.compileTpl(tpls[i]);
58663             this.tpls[tpls[i].id] = tpls[i];
58664         }
58665         this.master = tpls[tpls.length-1];
58666         return this;
58667     },
58668     /**
58669      * same as applyTemplate, except it's done to one of the subTemplates
58670      * when using named templates, you can do:
58671      *
58672      * var str = pl.applySubTemplate('your-name', values);
58673      *
58674      * 
58675      * @param {Number} id of the template
58676      * @param {Object} values to apply to template
58677      * @param {Object} parent (normaly the instance of this object)
58678      */
58679     applySubTemplate : function(id, values, parent)
58680     {
58681         
58682         
58683         var t = this.tpls[id];
58684         
58685         
58686         try { 
58687             if(t.test && !t.test.call(this, values, parent)){
58688                 return '';
58689             }
58690         } catch(e) {
58691             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58692             Roo.log(e.toString());
58693             Roo.log(t.test);
58694             return ''
58695         }
58696         try { 
58697             
58698             if(t.exec && t.exec.call(this, values, parent)){
58699                 return '';
58700             }
58701         } catch(e) {
58702             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58703             Roo.log(e.toString());
58704             Roo.log(t.exec);
58705             return ''
58706         }
58707         try {
58708             var vs = t.target ? t.target.call(this, values, parent) : values;
58709             parent = t.target ? values : parent;
58710             if(t.target && vs instanceof Array){
58711                 var buf = [];
58712                 for(var i = 0, len = vs.length; i < len; i++){
58713                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58714                 }
58715                 return buf.join('');
58716             }
58717             return t.compiled.call(this, vs, parent);
58718         } catch (e) {
58719             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58720             Roo.log(e.toString());
58721             Roo.log(t.compiled);
58722             return '';
58723         }
58724     },
58725
58726     compileTpl : function(tpl)
58727     {
58728         var fm = Roo.util.Format;
58729         var useF = this.disableFormats !== true;
58730         var sep = Roo.isGecko ? "+" : ",";
58731         var undef = function(str) {
58732             Roo.log("Property not found :"  + str);
58733             return '';
58734         };
58735         
58736         var fn = function(m, name, format, args)
58737         {
58738             //Roo.log(arguments);
58739             args = args ? args.replace(/\\'/g,"'") : args;
58740             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58741             if (typeof(format) == 'undefined') {
58742                 format= 'htmlEncode';
58743             }
58744             if (format == 'raw' ) {
58745                 format = false;
58746             }
58747             
58748             if(name.substr(0, 4) == 'xtpl'){
58749                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58750             }
58751             
58752             // build an array of options to determine if value is undefined..
58753             
58754             // basically get 'xxxx.yyyy' then do
58755             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58756             //    (function () { Roo.log("Property not found"); return ''; })() :
58757             //    ......
58758             
58759             var udef_ar = [];
58760             var lookfor = '';
58761             Roo.each(name.split('.'), function(st) {
58762                 lookfor += (lookfor.length ? '.': '') + st;
58763                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58764             });
58765             
58766             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58767             
58768             
58769             if(format && useF){
58770                 
58771                 args = args ? ',' + args : "";
58772                  
58773                 if(format.substr(0, 5) != "this."){
58774                     format = "fm." + format + '(';
58775                 }else{
58776                     format = 'this.call("'+ format.substr(5) + '", ';
58777                     args = ", values";
58778                 }
58779                 
58780                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58781             }
58782              
58783             if (args.length) {
58784                 // called with xxyx.yuu:(test,test)
58785                 // change to ()
58786                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58787             }
58788             // raw.. - :raw modifier..
58789             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58790             
58791         };
58792         var body;
58793         // branched to use + in gecko and [].join() in others
58794         if(Roo.isGecko){
58795             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58796                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58797                     "';};};";
58798         }else{
58799             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58800             body.push(tpl.body.replace(/(\r\n|\n)/g,
58801                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58802             body.push("'].join('');};};");
58803             body = body.join('');
58804         }
58805         
58806         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58807        
58808         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58809         eval(body);
58810         
58811         return this;
58812     },
58813
58814     applyTemplate : function(values){
58815         return this.master.compiled.call(this, values, {});
58816         //var s = this.subs;
58817     },
58818
58819     apply : function(){
58820         return this.applyTemplate.apply(this, arguments);
58821     }
58822
58823  });
58824
58825 Roo.XTemplate.from = function(el){
58826     el = Roo.getDom(el);
58827     return new Roo.XTemplate(el.value || el.innerHTML);
58828 };