roojs-ui.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         //disabling touch- as it's causing issues ..
35700         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35701         
35702         ul.on("mouseover", this.onMouseOver, this);
35703         ul.on("mouseout", this.onMouseOut, this);
35704         this.items.each(function(item){
35705             if (item.hidden) {
35706                 return;
35707             }
35708             
35709             var li = document.createElement("li");
35710             li.className = "x-menu-list-item";
35711             ul.dom.appendChild(li);
35712             item.render(li, this);
35713         }, this);
35714         this.ul = ul;
35715         this.autoWidth();
35716     },
35717
35718     // private
35719     autoWidth : function(){
35720         var el = this.el, ul = this.ul;
35721         if(!el){
35722             return;
35723         }
35724         var w = this.width;
35725         if(w){
35726             el.setWidth(w);
35727         }else if(Roo.isIE){
35728             el.setWidth(this.minWidth);
35729             var t = el.dom.offsetWidth; // force recalc
35730             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35731         }
35732     },
35733
35734     // private
35735     delayAutoWidth : function(){
35736         if(this.rendered){
35737             if(!this.awTask){
35738                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35739             }
35740             this.awTask.delay(20);
35741         }
35742     },
35743
35744     // private
35745     findTargetItem : function(e){
35746         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35747         if(t && t.menuItemId){
35748             return this.items.get(t.menuItemId);
35749         }
35750     },
35751
35752     // private
35753     onClick : function(e){
35754         Roo.log("menu.onClick");
35755         var t = this.findTargetItem(e);
35756         if(!t){
35757             return;
35758         }
35759         Roo.log(e);
35760         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35761             if(t == this.activeItem && t.shouldDeactivate(e)){
35762                 this.activeItem.deactivate();
35763                 delete this.activeItem;
35764                 return;
35765             }
35766             if(t.canActivate){
35767                 this.setActiveItem(t, true);
35768             }
35769             return;
35770             
35771             
35772         }
35773         
35774         t.onClick(e);
35775         this.fireEvent("click", this, t, e);
35776     },
35777
35778     // private
35779     setActiveItem : function(item, autoExpand){
35780         if(item != this.activeItem){
35781             if(this.activeItem){
35782                 this.activeItem.deactivate();
35783             }
35784             this.activeItem = item;
35785             item.activate(autoExpand);
35786         }else if(autoExpand){
35787             item.expandMenu();
35788         }
35789     },
35790
35791     // private
35792     tryActivate : function(start, step){
35793         var items = this.items;
35794         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35795             var item = items.get(i);
35796             if(!item.disabled && item.canActivate){
35797                 this.setActiveItem(item, false);
35798                 return item;
35799             }
35800         }
35801         return false;
35802     },
35803
35804     // private
35805     onMouseOver : function(e){
35806         var t;
35807         if(t = this.findTargetItem(e)){
35808             if(t.canActivate && !t.disabled){
35809                 this.setActiveItem(t, true);
35810             }
35811         }
35812         this.fireEvent("mouseover", this, e, t);
35813     },
35814
35815     // private
35816     onMouseOut : function(e){
35817         var t;
35818         if(t = this.findTargetItem(e)){
35819             if(t == this.activeItem && t.shouldDeactivate(e)){
35820                 this.activeItem.deactivate();
35821                 delete this.activeItem;
35822             }
35823         }
35824         this.fireEvent("mouseout", this, e, t);
35825     },
35826
35827     /**
35828      * Read-only.  Returns true if the menu is currently displayed, else false.
35829      * @type Boolean
35830      */
35831     isVisible : function(){
35832         return this.el && !this.hidden;
35833     },
35834
35835     /**
35836      * Displays this menu relative to another element
35837      * @param {String/HTMLElement/Roo.Element} element The element to align to
35838      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35839      * the element (defaults to this.defaultAlign)
35840      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35841      */
35842     show : function(el, pos, parentMenu){
35843         this.parentMenu = parentMenu;
35844         if(!this.el){
35845             this.render();
35846         }
35847         this.fireEvent("beforeshow", this);
35848         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35849     },
35850
35851     /**
35852      * Displays this menu at a specific xy position
35853      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35854      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35855      */
35856     showAt : function(xy, parentMenu, /* private: */_e){
35857         this.parentMenu = parentMenu;
35858         if(!this.el){
35859             this.render();
35860         }
35861         if(_e !== false){
35862             this.fireEvent("beforeshow", this);
35863             xy = this.el.adjustForConstraints(xy);
35864         }
35865         this.el.setXY(xy);
35866         this.el.show();
35867         this.hidden = false;
35868         this.focus();
35869         this.fireEvent("show", this);
35870     },
35871
35872     focus : function(){
35873         if(!this.hidden){
35874             this.doFocus.defer(50, this);
35875         }
35876     },
35877
35878     doFocus : function(){
35879         if(!this.hidden){
35880             this.focusEl.focus();
35881         }
35882     },
35883
35884     /**
35885      * Hides this menu and optionally all parent menus
35886      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35887      */
35888     hide : function(deep){
35889         if(this.el && this.isVisible()){
35890             this.fireEvent("beforehide", this);
35891             if(this.activeItem){
35892                 this.activeItem.deactivate();
35893                 this.activeItem = null;
35894             }
35895             this.el.hide();
35896             this.hidden = true;
35897             this.fireEvent("hide", this);
35898         }
35899         if(deep === true && this.parentMenu){
35900             this.parentMenu.hide(true);
35901         }
35902     },
35903
35904     /**
35905      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35906      * Any of the following are valid:
35907      * <ul>
35908      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35909      * <li>An HTMLElement object which will be converted to a menu item</li>
35910      * <li>A menu item config object that will be created as a new menu item</li>
35911      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35912      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35913      * </ul>
35914      * Usage:
35915      * <pre><code>
35916 // Create the menu
35917 var menu = new Roo.menu.Menu();
35918
35919 // Create a menu item to add by reference
35920 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35921
35922 // Add a bunch of items at once using different methods.
35923 // Only the last item added will be returned.
35924 var item = menu.add(
35925     menuItem,                // add existing item by ref
35926     'Dynamic Item',          // new TextItem
35927     '-',                     // new separator
35928     { text: 'Config Item' }  // new item by config
35929 );
35930 </code></pre>
35931      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35932      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35933      */
35934     add : function(){
35935         var a = arguments, l = a.length, item;
35936         for(var i = 0; i < l; i++){
35937             var el = a[i];
35938             if ((typeof(el) == "object") && el.xtype && el.xns) {
35939                 el = Roo.factory(el, Roo.menu);
35940             }
35941             
35942             if(el.render){ // some kind of Item
35943                 item = this.addItem(el);
35944             }else if(typeof el == "string"){ // string
35945                 if(el == "separator" || el == "-"){
35946                     item = this.addSeparator();
35947                 }else{
35948                     item = this.addText(el);
35949                 }
35950             }else if(el.tagName || el.el){ // element
35951                 item = this.addElement(el);
35952             }else if(typeof el == "object"){ // must be menu item config?
35953                 item = this.addMenuItem(el);
35954             }
35955         }
35956         return item;
35957     },
35958
35959     /**
35960      * Returns this menu's underlying {@link Roo.Element} object
35961      * @return {Roo.Element} The element
35962      */
35963     getEl : function(){
35964         if(!this.el){
35965             this.render();
35966         }
35967         return this.el;
35968     },
35969
35970     /**
35971      * Adds a separator bar to the menu
35972      * @return {Roo.menu.Item} The menu item that was added
35973      */
35974     addSeparator : function(){
35975         return this.addItem(new Roo.menu.Separator());
35976     },
35977
35978     /**
35979      * Adds an {@link Roo.Element} object to the menu
35980      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35981      * @return {Roo.menu.Item} The menu item that was added
35982      */
35983     addElement : function(el){
35984         return this.addItem(new Roo.menu.BaseItem(el));
35985     },
35986
35987     /**
35988      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35989      * @param {Roo.menu.Item} item The menu item to add
35990      * @return {Roo.menu.Item} The menu item that was added
35991      */
35992     addItem : function(item){
35993         this.items.add(item);
35994         if(this.ul){
35995             var li = document.createElement("li");
35996             li.className = "x-menu-list-item";
35997             this.ul.dom.appendChild(li);
35998             item.render(li, this);
35999             this.delayAutoWidth();
36000         }
36001         return item;
36002     },
36003
36004     /**
36005      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
36006      * @param {Object} config A MenuItem config object
36007      * @return {Roo.menu.Item} The menu item that was added
36008      */
36009     addMenuItem : function(config){
36010         if(!(config instanceof Roo.menu.Item)){
36011             if(typeof config.checked == "boolean"){ // must be check menu item config?
36012                 config = new Roo.menu.CheckItem(config);
36013             }else{
36014                 config = new Roo.menu.Item(config);
36015             }
36016         }
36017         return this.addItem(config);
36018     },
36019
36020     /**
36021      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36022      * @param {String} text The text to display in the menu item
36023      * @return {Roo.menu.Item} The menu item that was added
36024      */
36025     addText : function(text){
36026         return this.addItem(new Roo.menu.TextItem({ text : text }));
36027     },
36028
36029     /**
36030      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36031      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36032      * @param {Roo.menu.Item} item The menu item to add
36033      * @return {Roo.menu.Item} The menu item that was added
36034      */
36035     insert : function(index, item){
36036         this.items.insert(index, item);
36037         if(this.ul){
36038             var li = document.createElement("li");
36039             li.className = "x-menu-list-item";
36040             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36041             item.render(li, this);
36042             this.delayAutoWidth();
36043         }
36044         return item;
36045     },
36046
36047     /**
36048      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36049      * @param {Roo.menu.Item} item The menu item to remove
36050      */
36051     remove : function(item){
36052         this.items.removeKey(item.id);
36053         item.destroy();
36054     },
36055
36056     /**
36057      * Removes and destroys all items in the menu
36058      */
36059     removeAll : function(){
36060         var f;
36061         while(f = this.items.first()){
36062             this.remove(f);
36063         }
36064     }
36065 });
36066
36067 // MenuNav is a private utility class used internally by the Menu
36068 Roo.menu.MenuNav = function(menu){
36069     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36070     this.scope = this.menu = menu;
36071 };
36072
36073 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36074     doRelay : function(e, h){
36075         var k = e.getKey();
36076         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36077             this.menu.tryActivate(0, 1);
36078             return false;
36079         }
36080         return h.call(this.scope || this, e, this.menu);
36081     },
36082
36083     up : function(e, m){
36084         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36085             m.tryActivate(m.items.length-1, -1);
36086         }
36087     },
36088
36089     down : function(e, m){
36090         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36091             m.tryActivate(0, 1);
36092         }
36093     },
36094
36095     right : function(e, m){
36096         if(m.activeItem){
36097             m.activeItem.expandMenu(true);
36098         }
36099     },
36100
36101     left : function(e, m){
36102         m.hide();
36103         if(m.parentMenu && m.parentMenu.activeItem){
36104             m.parentMenu.activeItem.activate();
36105         }
36106     },
36107
36108     enter : function(e, m){
36109         if(m.activeItem){
36110             e.stopPropagation();
36111             m.activeItem.onClick(e);
36112             m.fireEvent("click", this, m.activeItem);
36113             return true;
36114         }
36115     }
36116 });/*
36117  * Based on:
36118  * Ext JS Library 1.1.1
36119  * Copyright(c) 2006-2007, Ext JS, LLC.
36120  *
36121  * Originally Released Under LGPL - original licence link has changed is not relivant.
36122  *
36123  * Fork - LGPL
36124  * <script type="text/javascript">
36125  */
36126  
36127 /**
36128  * @class Roo.menu.MenuMgr
36129  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36130  * @singleton
36131  */
36132 Roo.menu.MenuMgr = function(){
36133    var menus, active, groups = {}, attached = false, lastShow = new Date();
36134
36135    // private - called when first menu is created
36136    function init(){
36137        menus = {};
36138        active = new Roo.util.MixedCollection();
36139        Roo.get(document).addKeyListener(27, function(){
36140            if(active.length > 0){
36141                hideAll();
36142            }
36143        });
36144    }
36145
36146    // private
36147    function hideAll(){
36148        if(active && active.length > 0){
36149            var c = active.clone();
36150            c.each(function(m){
36151                m.hide();
36152            });
36153        }
36154    }
36155
36156    // private
36157    function onHide(m){
36158        active.remove(m);
36159        if(active.length < 1){
36160            Roo.get(document).un("mousedown", onMouseDown);
36161            attached = false;
36162        }
36163    }
36164
36165    // private
36166    function onShow(m){
36167        var last = active.last();
36168        lastShow = new Date();
36169        active.add(m);
36170        if(!attached){
36171            Roo.get(document).on("mousedown", onMouseDown);
36172            attached = true;
36173        }
36174        if(m.parentMenu){
36175           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36176           m.parentMenu.activeChild = m;
36177        }else if(last && last.isVisible()){
36178           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36179        }
36180    }
36181
36182    // private
36183    function onBeforeHide(m){
36184        if(m.activeChild){
36185            m.activeChild.hide();
36186        }
36187        if(m.autoHideTimer){
36188            clearTimeout(m.autoHideTimer);
36189            delete m.autoHideTimer;
36190        }
36191    }
36192
36193    // private
36194    function onBeforeShow(m){
36195        var pm = m.parentMenu;
36196        if(!pm && !m.allowOtherMenus){
36197            hideAll();
36198        }else if(pm && pm.activeChild && active != m){
36199            pm.activeChild.hide();
36200        }
36201    }
36202
36203    // private
36204    function onMouseDown(e){
36205        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36206            hideAll();
36207        }
36208    }
36209
36210    // private
36211    function onBeforeCheck(mi, state){
36212        if(state){
36213            var g = groups[mi.group];
36214            for(var i = 0, l = g.length; i < l; i++){
36215                if(g[i] != mi){
36216                    g[i].setChecked(false);
36217                }
36218            }
36219        }
36220    }
36221
36222    return {
36223
36224        /**
36225         * Hides all menus that are currently visible
36226         */
36227        hideAll : function(){
36228             hideAll();  
36229        },
36230
36231        // private
36232        register : function(menu){
36233            if(!menus){
36234                init();
36235            }
36236            menus[menu.id] = menu;
36237            menu.on("beforehide", onBeforeHide);
36238            menu.on("hide", onHide);
36239            menu.on("beforeshow", onBeforeShow);
36240            menu.on("show", onShow);
36241            var g = menu.group;
36242            if(g && menu.events["checkchange"]){
36243                if(!groups[g]){
36244                    groups[g] = [];
36245                }
36246                groups[g].push(menu);
36247                menu.on("checkchange", onCheck);
36248            }
36249        },
36250
36251         /**
36252          * Returns a {@link Roo.menu.Menu} object
36253          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36254          * be used to generate and return a new Menu instance.
36255          */
36256        get : function(menu){
36257            if(typeof menu == "string"){ // menu id
36258                return menus[menu];
36259            }else if(menu.events){  // menu instance
36260                return menu;
36261            }else if(typeof menu.length == 'number'){ // array of menu items?
36262                return new Roo.menu.Menu({items:menu});
36263            }else{ // otherwise, must be a config
36264                return new Roo.menu.Menu(menu);
36265            }
36266        },
36267
36268        // private
36269        unregister : function(menu){
36270            delete menus[menu.id];
36271            menu.un("beforehide", onBeforeHide);
36272            menu.un("hide", onHide);
36273            menu.un("beforeshow", onBeforeShow);
36274            menu.un("show", onShow);
36275            var g = menu.group;
36276            if(g && menu.events["checkchange"]){
36277                groups[g].remove(menu);
36278                menu.un("checkchange", onCheck);
36279            }
36280        },
36281
36282        // private
36283        registerCheckable : function(menuItem){
36284            var g = menuItem.group;
36285            if(g){
36286                if(!groups[g]){
36287                    groups[g] = [];
36288                }
36289                groups[g].push(menuItem);
36290                menuItem.on("beforecheckchange", onBeforeCheck);
36291            }
36292        },
36293
36294        // private
36295        unregisterCheckable : function(menuItem){
36296            var g = menuItem.group;
36297            if(g){
36298                groups[g].remove(menuItem);
36299                menuItem.un("beforecheckchange", onBeforeCheck);
36300            }
36301        }
36302    };
36303 }();/*
36304  * Based on:
36305  * Ext JS Library 1.1.1
36306  * Copyright(c) 2006-2007, Ext JS, LLC.
36307  *
36308  * Originally Released Under LGPL - original licence link has changed is not relivant.
36309  *
36310  * Fork - LGPL
36311  * <script type="text/javascript">
36312  */
36313  
36314
36315 /**
36316  * @class Roo.menu.BaseItem
36317  * @extends Roo.Component
36318  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36319  * management and base configuration options shared by all menu components.
36320  * @constructor
36321  * Creates a new BaseItem
36322  * @param {Object} config Configuration options
36323  */
36324 Roo.menu.BaseItem = function(config){
36325     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36326
36327     this.addEvents({
36328         /**
36329          * @event click
36330          * Fires when this item is clicked
36331          * @param {Roo.menu.BaseItem} this
36332          * @param {Roo.EventObject} e
36333          */
36334         click: true,
36335         /**
36336          * @event activate
36337          * Fires when this item is activated
36338          * @param {Roo.menu.BaseItem} this
36339          */
36340         activate : true,
36341         /**
36342          * @event deactivate
36343          * Fires when this item is deactivated
36344          * @param {Roo.menu.BaseItem} this
36345          */
36346         deactivate : true
36347     });
36348
36349     if(this.handler){
36350         this.on("click", this.handler, this.scope, true);
36351     }
36352 };
36353
36354 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36355     /**
36356      * @cfg {Function} handler
36357      * A function that will handle the click event of this menu item (defaults to undefined)
36358      */
36359     /**
36360      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36361      */
36362     canActivate : false,
36363     
36364      /**
36365      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36366      */
36367     hidden: false,
36368     
36369     /**
36370      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36371      */
36372     activeClass : "x-menu-item-active",
36373     /**
36374      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36375      */
36376     hideOnClick : true,
36377     /**
36378      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36379      */
36380     hideDelay : 100,
36381
36382     // private
36383     ctype: "Roo.menu.BaseItem",
36384
36385     // private
36386     actionMode : "container",
36387
36388     // private
36389     render : function(container, parentMenu){
36390         this.parentMenu = parentMenu;
36391         Roo.menu.BaseItem.superclass.render.call(this, container);
36392         this.container.menuItemId = this.id;
36393     },
36394
36395     // private
36396     onRender : function(container, position){
36397         this.el = Roo.get(this.el);
36398         container.dom.appendChild(this.el.dom);
36399     },
36400
36401     // private
36402     onClick : function(e){
36403         if(!this.disabled && this.fireEvent("click", this, e) !== false
36404                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36405             this.handleClick(e);
36406         }else{
36407             e.stopEvent();
36408         }
36409     },
36410
36411     // private
36412     activate : function(){
36413         if(this.disabled){
36414             return false;
36415         }
36416         var li = this.container;
36417         li.addClass(this.activeClass);
36418         this.region = li.getRegion().adjust(2, 2, -2, -2);
36419         this.fireEvent("activate", this);
36420         return true;
36421     },
36422
36423     // private
36424     deactivate : function(){
36425         this.container.removeClass(this.activeClass);
36426         this.fireEvent("deactivate", this);
36427     },
36428
36429     // private
36430     shouldDeactivate : function(e){
36431         return !this.region || !this.region.contains(e.getPoint());
36432     },
36433
36434     // private
36435     handleClick : function(e){
36436         if(this.hideOnClick){
36437             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36438         }
36439     },
36440
36441     // private
36442     expandMenu : function(autoActivate){
36443         // do nothing
36444     },
36445
36446     // private
36447     hideMenu : function(){
36448         // do nothing
36449     }
36450 });/*
36451  * Based on:
36452  * Ext JS Library 1.1.1
36453  * Copyright(c) 2006-2007, Ext JS, LLC.
36454  *
36455  * Originally Released Under LGPL - original licence link has changed is not relivant.
36456  *
36457  * Fork - LGPL
36458  * <script type="text/javascript">
36459  */
36460  
36461 /**
36462  * @class Roo.menu.Adapter
36463  * @extends Roo.menu.BaseItem
36464  * 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.
36465  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36466  * @constructor
36467  * Creates a new Adapter
36468  * @param {Object} config Configuration options
36469  */
36470 Roo.menu.Adapter = function(component, config){
36471     Roo.menu.Adapter.superclass.constructor.call(this, config);
36472     this.component = component;
36473 };
36474 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36475     // private
36476     canActivate : true,
36477
36478     // private
36479     onRender : function(container, position){
36480         this.component.render(container);
36481         this.el = this.component.getEl();
36482     },
36483
36484     // private
36485     activate : function(){
36486         if(this.disabled){
36487             return false;
36488         }
36489         this.component.focus();
36490         this.fireEvent("activate", this);
36491         return true;
36492     },
36493
36494     // private
36495     deactivate : function(){
36496         this.fireEvent("deactivate", this);
36497     },
36498
36499     // private
36500     disable : function(){
36501         this.component.disable();
36502         Roo.menu.Adapter.superclass.disable.call(this);
36503     },
36504
36505     // private
36506     enable : function(){
36507         this.component.enable();
36508         Roo.menu.Adapter.superclass.enable.call(this);
36509     }
36510 });/*
36511  * Based on:
36512  * Ext JS Library 1.1.1
36513  * Copyright(c) 2006-2007, Ext JS, LLC.
36514  *
36515  * Originally Released Under LGPL - original licence link has changed is not relivant.
36516  *
36517  * Fork - LGPL
36518  * <script type="text/javascript">
36519  */
36520
36521 /**
36522  * @class Roo.menu.TextItem
36523  * @extends Roo.menu.BaseItem
36524  * Adds a static text string to a menu, usually used as either a heading or group separator.
36525  * Note: old style constructor with text is still supported.
36526  * 
36527  * @constructor
36528  * Creates a new TextItem
36529  * @param {Object} cfg Configuration
36530  */
36531 Roo.menu.TextItem = function(cfg){
36532     if (typeof(cfg) == 'string') {
36533         this.text = cfg;
36534     } else {
36535         Roo.apply(this,cfg);
36536     }
36537     
36538     Roo.menu.TextItem.superclass.constructor.call(this);
36539 };
36540
36541 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36542     /**
36543      * @cfg {Boolean} text Text to show on item.
36544      */
36545     text : '',
36546     
36547     /**
36548      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36549      */
36550     hideOnClick : false,
36551     /**
36552      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36553      */
36554     itemCls : "x-menu-text",
36555
36556     // private
36557     onRender : function(){
36558         var s = document.createElement("span");
36559         s.className = this.itemCls;
36560         s.innerHTML = this.text;
36561         this.el = s;
36562         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36563     }
36564 });/*
36565  * Based on:
36566  * Ext JS Library 1.1.1
36567  * Copyright(c) 2006-2007, Ext JS, LLC.
36568  *
36569  * Originally Released Under LGPL - original licence link has changed is not relivant.
36570  *
36571  * Fork - LGPL
36572  * <script type="text/javascript">
36573  */
36574
36575 /**
36576  * @class Roo.menu.Separator
36577  * @extends Roo.menu.BaseItem
36578  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36579  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36580  * @constructor
36581  * @param {Object} config Configuration options
36582  */
36583 Roo.menu.Separator = function(config){
36584     Roo.menu.Separator.superclass.constructor.call(this, config);
36585 };
36586
36587 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36588     /**
36589      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36590      */
36591     itemCls : "x-menu-sep",
36592     /**
36593      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36594      */
36595     hideOnClick : false,
36596
36597     // private
36598     onRender : function(li){
36599         var s = document.createElement("span");
36600         s.className = this.itemCls;
36601         s.innerHTML = "&#160;";
36602         this.el = s;
36603         li.addClass("x-menu-sep-li");
36604         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36605     }
36606 });/*
36607  * Based on:
36608  * Ext JS Library 1.1.1
36609  * Copyright(c) 2006-2007, Ext JS, LLC.
36610  *
36611  * Originally Released Under LGPL - original licence link has changed is not relivant.
36612  *
36613  * Fork - LGPL
36614  * <script type="text/javascript">
36615  */
36616 /**
36617  * @class Roo.menu.Item
36618  * @extends Roo.menu.BaseItem
36619  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36620  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36621  * activation and click handling.
36622  * @constructor
36623  * Creates a new Item
36624  * @param {Object} config Configuration options
36625  */
36626 Roo.menu.Item = function(config){
36627     Roo.menu.Item.superclass.constructor.call(this, config);
36628     if(this.menu){
36629         this.menu = Roo.menu.MenuMgr.get(this.menu);
36630     }
36631 };
36632 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36633     
36634     /**
36635      * @cfg {String} text
36636      * The text to show on the menu item.
36637      */
36638     text: '',
36639      /**
36640      * @cfg {String} HTML to render in menu
36641      * The text to show on the menu item (HTML version).
36642      */
36643     html: '',
36644     /**
36645      * @cfg {String} icon
36646      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36647      */
36648     icon: undefined,
36649     /**
36650      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36651      */
36652     itemCls : "x-menu-item",
36653     /**
36654      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36655      */
36656     canActivate : true,
36657     /**
36658      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36659      */
36660     showDelay: 200,
36661     // doc'd in BaseItem
36662     hideDelay: 200,
36663
36664     // private
36665     ctype: "Roo.menu.Item",
36666     
36667     // private
36668     onRender : function(container, position){
36669         var el = document.createElement("a");
36670         el.hideFocus = true;
36671         el.unselectable = "on";
36672         el.href = this.href || "#";
36673         if(this.hrefTarget){
36674             el.target = this.hrefTarget;
36675         }
36676         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36677         
36678         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36679         
36680         el.innerHTML = String.format(
36681                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36682                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36683         this.el = el;
36684         Roo.menu.Item.superclass.onRender.call(this, container, position);
36685     },
36686
36687     /**
36688      * Sets the text to display in this menu item
36689      * @param {String} text The text to display
36690      * @param {Boolean} isHTML true to indicate text is pure html.
36691      */
36692     setText : function(text, isHTML){
36693         if (isHTML) {
36694             this.html = text;
36695         } else {
36696             this.text = text;
36697             this.html = '';
36698         }
36699         if(this.rendered){
36700             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36701      
36702             this.el.update(String.format(
36703                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36704                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36705             this.parentMenu.autoWidth();
36706         }
36707     },
36708
36709     // private
36710     handleClick : function(e){
36711         if(!this.href){ // if no link defined, stop the event automatically
36712             e.stopEvent();
36713         }
36714         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36715     },
36716
36717     // private
36718     activate : function(autoExpand){
36719         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36720             this.focus();
36721             if(autoExpand){
36722                 this.expandMenu();
36723             }
36724         }
36725         return true;
36726     },
36727
36728     // private
36729     shouldDeactivate : function(e){
36730         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36731             if(this.menu && this.menu.isVisible()){
36732                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36733             }
36734             return true;
36735         }
36736         return false;
36737     },
36738
36739     // private
36740     deactivate : function(){
36741         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36742         this.hideMenu();
36743     },
36744
36745     // private
36746     expandMenu : function(autoActivate){
36747         if(!this.disabled && this.menu){
36748             clearTimeout(this.hideTimer);
36749             delete this.hideTimer;
36750             if(!this.menu.isVisible() && !this.showTimer){
36751                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36752             }else if (this.menu.isVisible() && autoActivate){
36753                 this.menu.tryActivate(0, 1);
36754             }
36755         }
36756     },
36757
36758     // private
36759     deferExpand : function(autoActivate){
36760         delete this.showTimer;
36761         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36762         if(autoActivate){
36763             this.menu.tryActivate(0, 1);
36764         }
36765     },
36766
36767     // private
36768     hideMenu : function(){
36769         clearTimeout(this.showTimer);
36770         delete this.showTimer;
36771         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36772             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36773         }
36774     },
36775
36776     // private
36777     deferHide : function(){
36778         delete this.hideTimer;
36779         this.menu.hide();
36780     }
36781 });/*
36782  * Based on:
36783  * Ext JS Library 1.1.1
36784  * Copyright(c) 2006-2007, Ext JS, LLC.
36785  *
36786  * Originally Released Under LGPL - original licence link has changed is not relivant.
36787  *
36788  * Fork - LGPL
36789  * <script type="text/javascript">
36790  */
36791  
36792 /**
36793  * @class Roo.menu.CheckItem
36794  * @extends Roo.menu.Item
36795  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36796  * @constructor
36797  * Creates a new CheckItem
36798  * @param {Object} config Configuration options
36799  */
36800 Roo.menu.CheckItem = function(config){
36801     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36802     this.addEvents({
36803         /**
36804          * @event beforecheckchange
36805          * Fires before the checked value is set, providing an opportunity to cancel if needed
36806          * @param {Roo.menu.CheckItem} this
36807          * @param {Boolean} checked The new checked value that will be set
36808          */
36809         "beforecheckchange" : true,
36810         /**
36811          * @event checkchange
36812          * Fires after the checked value has been set
36813          * @param {Roo.menu.CheckItem} this
36814          * @param {Boolean} checked The checked value that was set
36815          */
36816         "checkchange" : true
36817     });
36818     if(this.checkHandler){
36819         this.on('checkchange', this.checkHandler, this.scope);
36820     }
36821 };
36822 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36823     /**
36824      * @cfg {String} group
36825      * All check items with the same group name will automatically be grouped into a single-select
36826      * radio button group (defaults to '')
36827      */
36828     /**
36829      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36830      */
36831     itemCls : "x-menu-item x-menu-check-item",
36832     /**
36833      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36834      */
36835     groupClass : "x-menu-group-item",
36836
36837     /**
36838      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36839      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36840      * initialized with checked = true will be rendered as checked.
36841      */
36842     checked: false,
36843
36844     // private
36845     ctype: "Roo.menu.CheckItem",
36846
36847     // private
36848     onRender : function(c){
36849         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36850         if(this.group){
36851             this.el.addClass(this.groupClass);
36852         }
36853         Roo.menu.MenuMgr.registerCheckable(this);
36854         if(this.checked){
36855             this.checked = false;
36856             this.setChecked(true, true);
36857         }
36858     },
36859
36860     // private
36861     destroy : function(){
36862         if(this.rendered){
36863             Roo.menu.MenuMgr.unregisterCheckable(this);
36864         }
36865         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36866     },
36867
36868     /**
36869      * Set the checked state of this item
36870      * @param {Boolean} checked The new checked value
36871      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36872      */
36873     setChecked : function(state, suppressEvent){
36874         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36875             if(this.container){
36876                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36877             }
36878             this.checked = state;
36879             if(suppressEvent !== true){
36880                 this.fireEvent("checkchange", this, state);
36881             }
36882         }
36883     },
36884
36885     // private
36886     handleClick : function(e){
36887        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36888            this.setChecked(!this.checked);
36889        }
36890        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36891     }
36892 });/*
36893  * Based on:
36894  * Ext JS Library 1.1.1
36895  * Copyright(c) 2006-2007, Ext JS, LLC.
36896  *
36897  * Originally Released Under LGPL - original licence link has changed is not relivant.
36898  *
36899  * Fork - LGPL
36900  * <script type="text/javascript">
36901  */
36902  
36903 /**
36904  * @class Roo.menu.DateItem
36905  * @extends Roo.menu.Adapter
36906  * A menu item that wraps the {@link Roo.DatPicker} component.
36907  * @constructor
36908  * Creates a new DateItem
36909  * @param {Object} config Configuration options
36910  */
36911 Roo.menu.DateItem = function(config){
36912     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36913     /** The Roo.DatePicker object @type Roo.DatePicker */
36914     this.picker = this.component;
36915     this.addEvents({select: true});
36916     
36917     this.picker.on("render", function(picker){
36918         picker.getEl().swallowEvent("click");
36919         picker.container.addClass("x-menu-date-item");
36920     });
36921
36922     this.picker.on("select", this.onSelect, this);
36923 };
36924
36925 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36926     // private
36927     onSelect : function(picker, date){
36928         this.fireEvent("select", this, date, picker);
36929         Roo.menu.DateItem.superclass.handleClick.call(this);
36930     }
36931 });/*
36932  * Based on:
36933  * Ext JS Library 1.1.1
36934  * Copyright(c) 2006-2007, Ext JS, LLC.
36935  *
36936  * Originally Released Under LGPL - original licence link has changed is not relivant.
36937  *
36938  * Fork - LGPL
36939  * <script type="text/javascript">
36940  */
36941  
36942 /**
36943  * @class Roo.menu.ColorItem
36944  * @extends Roo.menu.Adapter
36945  * A menu item that wraps the {@link Roo.ColorPalette} component.
36946  * @constructor
36947  * Creates a new ColorItem
36948  * @param {Object} config Configuration options
36949  */
36950 Roo.menu.ColorItem = function(config){
36951     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36952     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36953     this.palette = this.component;
36954     this.relayEvents(this.palette, ["select"]);
36955     if(this.selectHandler){
36956         this.on('select', this.selectHandler, this.scope);
36957     }
36958 };
36959 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36960  * Based on:
36961  * Ext JS Library 1.1.1
36962  * Copyright(c) 2006-2007, Ext JS, LLC.
36963  *
36964  * Originally Released Under LGPL - original licence link has changed is not relivant.
36965  *
36966  * Fork - LGPL
36967  * <script type="text/javascript">
36968  */
36969  
36970
36971 /**
36972  * @class Roo.menu.DateMenu
36973  * @extends Roo.menu.Menu
36974  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36975  * @constructor
36976  * Creates a new DateMenu
36977  * @param {Object} config Configuration options
36978  */
36979 Roo.menu.DateMenu = function(config){
36980     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36981     this.plain = true;
36982     var di = new Roo.menu.DateItem(config);
36983     this.add(di);
36984     /**
36985      * The {@link Roo.DatePicker} instance for this DateMenu
36986      * @type DatePicker
36987      */
36988     this.picker = di.picker;
36989     /**
36990      * @event select
36991      * @param {DatePicker} picker
36992      * @param {Date} date
36993      */
36994     this.relayEvents(di, ["select"]);
36995     this.on('beforeshow', function(){
36996         if(this.picker){
36997             this.picker.hideMonthPicker(false);
36998         }
36999     }, this);
37000 };
37001 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
37002     cls:'x-date-menu'
37003 });/*
37004  * Based on:
37005  * Ext JS Library 1.1.1
37006  * Copyright(c) 2006-2007, Ext JS, LLC.
37007  *
37008  * Originally Released Under LGPL - original licence link has changed is not relivant.
37009  *
37010  * Fork - LGPL
37011  * <script type="text/javascript">
37012  */
37013  
37014
37015 /**
37016  * @class Roo.menu.ColorMenu
37017  * @extends Roo.menu.Menu
37018  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37019  * @constructor
37020  * Creates a new ColorMenu
37021  * @param {Object} config Configuration options
37022  */
37023 Roo.menu.ColorMenu = function(config){
37024     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37025     this.plain = true;
37026     var ci = new Roo.menu.ColorItem(config);
37027     this.add(ci);
37028     /**
37029      * The {@link Roo.ColorPalette} instance for this ColorMenu
37030      * @type ColorPalette
37031      */
37032     this.palette = ci.palette;
37033     /**
37034      * @event select
37035      * @param {ColorPalette} palette
37036      * @param {String} color
37037      */
37038     this.relayEvents(ci, ["select"]);
37039 };
37040 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37041  * Based on:
37042  * Ext JS Library 1.1.1
37043  * Copyright(c) 2006-2007, Ext JS, LLC.
37044  *
37045  * Originally Released Under LGPL - original licence link has changed is not relivant.
37046  *
37047  * Fork - LGPL
37048  * <script type="text/javascript">
37049  */
37050  
37051 /**
37052  * @class Roo.form.Field
37053  * @extends Roo.BoxComponent
37054  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37055  * @constructor
37056  * Creates a new Field
37057  * @param {Object} config Configuration options
37058  */
37059 Roo.form.Field = function(config){
37060     Roo.form.Field.superclass.constructor.call(this, config);
37061 };
37062
37063 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37064     /**
37065      * @cfg {String} fieldLabel Label to use when rendering a form.
37066      */
37067        /**
37068      * @cfg {String} qtip Mouse over tip
37069      */
37070      
37071     /**
37072      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37073      */
37074     invalidClass : "x-form-invalid",
37075     /**
37076      * @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")
37077      */
37078     invalidText : "The value in this field is invalid",
37079     /**
37080      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37081      */
37082     focusClass : "x-form-focus",
37083     /**
37084      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37085       automatic validation (defaults to "keyup").
37086      */
37087     validationEvent : "keyup",
37088     /**
37089      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37090      */
37091     validateOnBlur : true,
37092     /**
37093      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37094      */
37095     validationDelay : 250,
37096     /**
37097      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37098      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37099      */
37100     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37101     /**
37102      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37103      */
37104     fieldClass : "x-form-field",
37105     /**
37106      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37107      *<pre>
37108 Value         Description
37109 -----------   ----------------------------------------------------------------------
37110 qtip          Display a quick tip when the user hovers over the field
37111 title         Display a default browser title attribute popup
37112 under         Add a block div beneath the field containing the error text
37113 side          Add an error icon to the right of the field with a popup on hover
37114 [element id]  Add the error text directly to the innerHTML of the specified element
37115 </pre>
37116      */
37117     msgTarget : 'qtip',
37118     /**
37119      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37120      */
37121     msgFx : 'normal',
37122
37123     /**
37124      * @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.
37125      */
37126     readOnly : false,
37127
37128     /**
37129      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37130      */
37131     disabled : false,
37132
37133     /**
37134      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37135      */
37136     inputType : undefined,
37137     
37138     /**
37139      * @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).
37140          */
37141         tabIndex : undefined,
37142         
37143     // private
37144     isFormField : true,
37145
37146     // private
37147     hasFocus : false,
37148     /**
37149      * @property {Roo.Element} fieldEl
37150      * Element Containing the rendered Field (with label etc.)
37151      */
37152     /**
37153      * @cfg {Mixed} value A value to initialize this field with.
37154      */
37155     value : undefined,
37156
37157     /**
37158      * @cfg {String} name The field's HTML name attribute.
37159      */
37160     /**
37161      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37162      */
37163
37164         // private ??
37165         initComponent : function(){
37166         Roo.form.Field.superclass.initComponent.call(this);
37167         this.addEvents({
37168             /**
37169              * @event focus
37170              * Fires when this field receives input focus.
37171              * @param {Roo.form.Field} this
37172              */
37173             focus : true,
37174             /**
37175              * @event blur
37176              * Fires when this field loses input focus.
37177              * @param {Roo.form.Field} this
37178              */
37179             blur : true,
37180             /**
37181              * @event specialkey
37182              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37183              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37184              * @param {Roo.form.Field} this
37185              * @param {Roo.EventObject} e The event object
37186              */
37187             specialkey : true,
37188             /**
37189              * @event change
37190              * Fires just before the field blurs if the field value has changed.
37191              * @param {Roo.form.Field} this
37192              * @param {Mixed} newValue The new value
37193              * @param {Mixed} oldValue The original value
37194              */
37195             change : true,
37196             /**
37197              * @event invalid
37198              * Fires after the field has been marked as invalid.
37199              * @param {Roo.form.Field} this
37200              * @param {String} msg The validation message
37201              */
37202             invalid : true,
37203             /**
37204              * @event valid
37205              * Fires after the field has been validated with no errors.
37206              * @param {Roo.form.Field} this
37207              */
37208             valid : true,
37209              /**
37210              * @event keyup
37211              * Fires after the key up
37212              * @param {Roo.form.Field} this
37213              * @param {Roo.EventObject}  e The event Object
37214              */
37215             keyup : true
37216         });
37217     },
37218
37219     /**
37220      * Returns the name attribute of the field if available
37221      * @return {String} name The field name
37222      */
37223     getName: function(){
37224          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37225     },
37226
37227     // private
37228     onRender : function(ct, position){
37229         Roo.form.Field.superclass.onRender.call(this, ct, position);
37230         if(!this.el){
37231             var cfg = this.getAutoCreate();
37232             if(!cfg.name){
37233                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37234             }
37235             if (!cfg.name.length) {
37236                 delete cfg.name;
37237             }
37238             if(this.inputType){
37239                 cfg.type = this.inputType;
37240             }
37241             this.el = ct.createChild(cfg, position);
37242         }
37243         var type = this.el.dom.type;
37244         if(type){
37245             if(type == 'password'){
37246                 type = 'text';
37247             }
37248             this.el.addClass('x-form-'+type);
37249         }
37250         if(this.readOnly){
37251             this.el.dom.readOnly = true;
37252         }
37253         if(this.tabIndex !== undefined){
37254             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37255         }
37256
37257         this.el.addClass([this.fieldClass, this.cls]);
37258         this.initValue();
37259     },
37260
37261     /**
37262      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37263      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37264      * @return {Roo.form.Field} this
37265      */
37266     applyTo : function(target){
37267         this.allowDomMove = false;
37268         this.el = Roo.get(target);
37269         this.render(this.el.dom.parentNode);
37270         return this;
37271     },
37272
37273     // private
37274     initValue : function(){
37275         if(this.value !== undefined){
37276             this.setValue(this.value);
37277         }else if(this.el.dom.value.length > 0){
37278             this.setValue(this.el.dom.value);
37279         }
37280     },
37281
37282     /**
37283      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37284      */
37285     isDirty : function() {
37286         if(this.disabled) {
37287             return false;
37288         }
37289         return String(this.getValue()) !== String(this.originalValue);
37290     },
37291
37292     // private
37293     afterRender : function(){
37294         Roo.form.Field.superclass.afterRender.call(this);
37295         this.initEvents();
37296     },
37297
37298     // private
37299     fireKey : function(e){
37300         //Roo.log('field ' + e.getKey());
37301         if(e.isNavKeyPress()){
37302             this.fireEvent("specialkey", this, e);
37303         }
37304     },
37305
37306     /**
37307      * Resets the current field value to the originally loaded value and clears any validation messages
37308      */
37309     reset : function(){
37310         this.setValue(this.resetValue);
37311         this.clearInvalid();
37312     },
37313
37314     // private
37315     initEvents : function(){
37316         // safari killled keypress - so keydown is now used..
37317         this.el.on("keydown" , this.fireKey,  this);
37318         this.el.on("focus", this.onFocus,  this);
37319         this.el.on("blur", this.onBlur,  this);
37320         this.el.relayEvent('keyup', this);
37321
37322         // reference to original value for reset
37323         this.originalValue = this.getValue();
37324         this.resetValue =  this.getValue();
37325     },
37326
37327     // private
37328     onFocus : function(){
37329         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37330             this.el.addClass(this.focusClass);
37331         }
37332         if(!this.hasFocus){
37333             this.hasFocus = true;
37334             this.startValue = this.getValue();
37335             this.fireEvent("focus", this);
37336         }
37337     },
37338
37339     beforeBlur : Roo.emptyFn,
37340
37341     // private
37342     onBlur : function(){
37343         this.beforeBlur();
37344         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37345             this.el.removeClass(this.focusClass);
37346         }
37347         this.hasFocus = false;
37348         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37349             this.validate();
37350         }
37351         var v = this.getValue();
37352         if(String(v) !== String(this.startValue)){
37353             this.fireEvent('change', this, v, this.startValue);
37354         }
37355         this.fireEvent("blur", this);
37356     },
37357
37358     /**
37359      * Returns whether or not the field value is currently valid
37360      * @param {Boolean} preventMark True to disable marking the field invalid
37361      * @return {Boolean} True if the value is valid, else false
37362      */
37363     isValid : function(preventMark){
37364         if(this.disabled){
37365             return true;
37366         }
37367         var restore = this.preventMark;
37368         this.preventMark = preventMark === true;
37369         var v = this.validateValue(this.processValue(this.getRawValue()));
37370         this.preventMark = restore;
37371         return v;
37372     },
37373
37374     /**
37375      * Validates the field value
37376      * @return {Boolean} True if the value is valid, else false
37377      */
37378     validate : function(){
37379         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37380             this.clearInvalid();
37381             return true;
37382         }
37383         return false;
37384     },
37385
37386     processValue : function(value){
37387         return value;
37388     },
37389
37390     // private
37391     // Subclasses should provide the validation implementation by overriding this
37392     validateValue : function(value){
37393         return true;
37394     },
37395
37396     /**
37397      * Mark this field as invalid
37398      * @param {String} msg The validation message
37399      */
37400     markInvalid : function(msg){
37401         if(!this.rendered || this.preventMark){ // not rendered
37402             return;
37403         }
37404         
37405         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37406         
37407         obj.el.addClass(this.invalidClass);
37408         msg = msg || this.invalidText;
37409         switch(this.msgTarget){
37410             case 'qtip':
37411                 obj.el.dom.qtip = msg;
37412                 obj.el.dom.qclass = 'x-form-invalid-tip';
37413                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37414                     Roo.QuickTips.enable();
37415                 }
37416                 break;
37417             case 'title':
37418                 this.el.dom.title = msg;
37419                 break;
37420             case 'under':
37421                 if(!this.errorEl){
37422                     var elp = this.el.findParent('.x-form-element', 5, true);
37423                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37424                     this.errorEl.setWidth(elp.getWidth(true)-20);
37425                 }
37426                 this.errorEl.update(msg);
37427                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37428                 break;
37429             case 'side':
37430                 if(!this.errorIcon){
37431                     var elp = this.el.findParent('.x-form-element', 5, true);
37432                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37433                 }
37434                 this.alignErrorIcon();
37435                 this.errorIcon.dom.qtip = msg;
37436                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37437                 this.errorIcon.show();
37438                 this.on('resize', this.alignErrorIcon, this);
37439                 break;
37440             default:
37441                 var t = Roo.getDom(this.msgTarget);
37442                 t.innerHTML = msg;
37443                 t.style.display = this.msgDisplay;
37444                 break;
37445         }
37446         this.fireEvent('invalid', this, msg);
37447     },
37448
37449     // private
37450     alignErrorIcon : function(){
37451         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37452     },
37453
37454     /**
37455      * Clear any invalid styles/messages for this field
37456      */
37457     clearInvalid : function(){
37458         if(!this.rendered || this.preventMark){ // not rendered
37459             return;
37460         }
37461         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37462         
37463         obj.el.removeClass(this.invalidClass);
37464         switch(this.msgTarget){
37465             case 'qtip':
37466                 obj.el.dom.qtip = '';
37467                 break;
37468             case 'title':
37469                 this.el.dom.title = '';
37470                 break;
37471             case 'under':
37472                 if(this.errorEl){
37473                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37474                 }
37475                 break;
37476             case 'side':
37477                 if(this.errorIcon){
37478                     this.errorIcon.dom.qtip = '';
37479                     this.errorIcon.hide();
37480                     this.un('resize', this.alignErrorIcon, this);
37481                 }
37482                 break;
37483             default:
37484                 var t = Roo.getDom(this.msgTarget);
37485                 t.innerHTML = '';
37486                 t.style.display = 'none';
37487                 break;
37488         }
37489         this.fireEvent('valid', this);
37490     },
37491
37492     /**
37493      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37494      * @return {Mixed} value The field value
37495      */
37496     getRawValue : function(){
37497         var v = this.el.getValue();
37498         
37499         return v;
37500     },
37501
37502     /**
37503      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37504      * @return {Mixed} value The field value
37505      */
37506     getValue : function(){
37507         var v = this.el.getValue();
37508          
37509         return v;
37510     },
37511
37512     /**
37513      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37514      * @param {Mixed} value The value to set
37515      */
37516     setRawValue : function(v){
37517         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37518     },
37519
37520     /**
37521      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37522      * @param {Mixed} value The value to set
37523      */
37524     setValue : function(v){
37525         this.value = v;
37526         if(this.rendered){
37527             this.el.dom.value = (v === null || v === undefined ? '' : v);
37528              this.validate();
37529         }
37530     },
37531
37532     adjustSize : function(w, h){
37533         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37534         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37535         return s;
37536     },
37537
37538     adjustWidth : function(tag, w){
37539         tag = tag.toLowerCase();
37540         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37541             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37542                 if(tag == 'input'){
37543                     return w + 2;
37544                 }
37545                 if(tag == 'textarea'){
37546                     return w-2;
37547                 }
37548             }else if(Roo.isOpera){
37549                 if(tag == 'input'){
37550                     return w + 2;
37551                 }
37552                 if(tag == 'textarea'){
37553                     return w-2;
37554                 }
37555             }
37556         }
37557         return w;
37558     }
37559 });
37560
37561
37562 // anything other than normal should be considered experimental
37563 Roo.form.Field.msgFx = {
37564     normal : {
37565         show: function(msgEl, f){
37566             msgEl.setDisplayed('block');
37567         },
37568
37569         hide : function(msgEl, f){
37570             msgEl.setDisplayed(false).update('');
37571         }
37572     },
37573
37574     slide : {
37575         show: function(msgEl, f){
37576             msgEl.slideIn('t', {stopFx:true});
37577         },
37578
37579         hide : function(msgEl, f){
37580             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37581         }
37582     },
37583
37584     slideRight : {
37585         show: function(msgEl, f){
37586             msgEl.fixDisplay();
37587             msgEl.alignTo(f.el, 'tl-tr');
37588             msgEl.slideIn('l', {stopFx:true});
37589         },
37590
37591         hide : function(msgEl, f){
37592             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37593         }
37594     }
37595 };/*
37596  * Based on:
37597  * Ext JS Library 1.1.1
37598  * Copyright(c) 2006-2007, Ext JS, LLC.
37599  *
37600  * Originally Released Under LGPL - original licence link has changed is not relivant.
37601  *
37602  * Fork - LGPL
37603  * <script type="text/javascript">
37604  */
37605  
37606
37607 /**
37608  * @class Roo.form.TextField
37609  * @extends Roo.form.Field
37610  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37611  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37612  * @constructor
37613  * Creates a new TextField
37614  * @param {Object} config Configuration options
37615  */
37616 Roo.form.TextField = function(config){
37617     Roo.form.TextField.superclass.constructor.call(this, config);
37618     this.addEvents({
37619         /**
37620          * @event autosize
37621          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37622          * according to the default logic, but this event provides a hook for the developer to apply additional
37623          * logic at runtime to resize the field if needed.
37624              * @param {Roo.form.Field} this This text field
37625              * @param {Number} width The new field width
37626              */
37627         autosize : true
37628     });
37629 };
37630
37631 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37632     /**
37633      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37634      */
37635     grow : false,
37636     /**
37637      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37638      */
37639     growMin : 30,
37640     /**
37641      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37642      */
37643     growMax : 800,
37644     /**
37645      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37646      */
37647     vtype : null,
37648     /**
37649      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37650      */
37651     maskRe : null,
37652     /**
37653      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37654      */
37655     disableKeyFilter : false,
37656     /**
37657      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37658      */
37659     allowBlank : true,
37660     /**
37661      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37662      */
37663     minLength : 0,
37664     /**
37665      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37666      */
37667     maxLength : Number.MAX_VALUE,
37668     /**
37669      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37670      */
37671     minLengthText : "The minimum length for this field is {0}",
37672     /**
37673      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37674      */
37675     maxLengthText : "The maximum length for this field is {0}",
37676     /**
37677      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37678      */
37679     selectOnFocus : false,
37680     /**
37681      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37682      */
37683     blankText : "This field is required",
37684     /**
37685      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37686      * If available, this function will be called only after the basic validators all return true, and will be passed the
37687      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37688      */
37689     validator : null,
37690     /**
37691      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37692      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37693      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37694      */
37695     regex : null,
37696     /**
37697      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37698      */
37699     regexText : "",
37700     /**
37701      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37702      */
37703     emptyText : null,
37704    
37705
37706     // private
37707     initEvents : function()
37708     {
37709         if (this.emptyText) {
37710             this.el.attr('placeholder', this.emptyText);
37711         }
37712         
37713         Roo.form.TextField.superclass.initEvents.call(this);
37714         if(this.validationEvent == 'keyup'){
37715             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37716             this.el.on('keyup', this.filterValidation, this);
37717         }
37718         else if(this.validationEvent !== false){
37719             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37720         }
37721         
37722         if(this.selectOnFocus){
37723             this.on("focus", this.preFocus, this);
37724             
37725         }
37726         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37727             this.el.on("keypress", this.filterKeys, this);
37728         }
37729         if(this.grow){
37730             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37731             this.el.on("click", this.autoSize,  this);
37732         }
37733         if(this.el.is('input[type=password]') && Roo.isSafari){
37734             this.el.on('keydown', this.SafariOnKeyDown, this);
37735         }
37736     },
37737
37738     processValue : function(value){
37739         if(this.stripCharsRe){
37740             var newValue = value.replace(this.stripCharsRe, '');
37741             if(newValue !== value){
37742                 this.setRawValue(newValue);
37743                 return newValue;
37744             }
37745         }
37746         return value;
37747     },
37748
37749     filterValidation : function(e){
37750         if(!e.isNavKeyPress()){
37751             this.validationTask.delay(this.validationDelay);
37752         }
37753     },
37754
37755     // private
37756     onKeyUp : function(e){
37757         if(!e.isNavKeyPress()){
37758             this.autoSize();
37759         }
37760     },
37761
37762     /**
37763      * Resets the current field value to the originally-loaded value and clears any validation messages.
37764      *  
37765      */
37766     reset : function(){
37767         Roo.form.TextField.superclass.reset.call(this);
37768        
37769     },
37770
37771     
37772     // private
37773     preFocus : function(){
37774         
37775         if(this.selectOnFocus){
37776             this.el.dom.select();
37777         }
37778     },
37779
37780     
37781     // private
37782     filterKeys : function(e){
37783         var k = e.getKey();
37784         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37785             return;
37786         }
37787         var c = e.getCharCode(), cc = String.fromCharCode(c);
37788         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37789             return;
37790         }
37791         if(!this.maskRe.test(cc)){
37792             e.stopEvent();
37793         }
37794     },
37795
37796     setValue : function(v){
37797         
37798         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37799         
37800         this.autoSize();
37801     },
37802
37803     /**
37804      * Validates a value according to the field's validation rules and marks the field as invalid
37805      * if the validation fails
37806      * @param {Mixed} value The value to validate
37807      * @return {Boolean} True if the value is valid, else false
37808      */
37809     validateValue : function(value){
37810         if(value.length < 1)  { // if it's blank
37811              if(this.allowBlank){
37812                 this.clearInvalid();
37813                 return true;
37814              }else{
37815                 this.markInvalid(this.blankText);
37816                 return false;
37817              }
37818         }
37819         if(value.length < this.minLength){
37820             this.markInvalid(String.format(this.minLengthText, this.minLength));
37821             return false;
37822         }
37823         if(value.length > this.maxLength){
37824             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37825             return false;
37826         }
37827         if(this.vtype){
37828             var vt = Roo.form.VTypes;
37829             if(!vt[this.vtype](value, this)){
37830                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37831                 return false;
37832             }
37833         }
37834         if(typeof this.validator == "function"){
37835             var msg = this.validator(value);
37836             if(msg !== true){
37837                 this.markInvalid(msg);
37838                 return false;
37839             }
37840         }
37841         if(this.regex && !this.regex.test(value)){
37842             this.markInvalid(this.regexText);
37843             return false;
37844         }
37845         return true;
37846     },
37847
37848     /**
37849      * Selects text in this field
37850      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37851      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37852      */
37853     selectText : function(start, end){
37854         var v = this.getRawValue();
37855         if(v.length > 0){
37856             start = start === undefined ? 0 : start;
37857             end = end === undefined ? v.length : end;
37858             var d = this.el.dom;
37859             if(d.setSelectionRange){
37860                 d.setSelectionRange(start, end);
37861             }else if(d.createTextRange){
37862                 var range = d.createTextRange();
37863                 range.moveStart("character", start);
37864                 range.moveEnd("character", v.length-end);
37865                 range.select();
37866             }
37867         }
37868     },
37869
37870     /**
37871      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37872      * This only takes effect if grow = true, and fires the autosize event.
37873      */
37874     autoSize : function(){
37875         if(!this.grow || !this.rendered){
37876             return;
37877         }
37878         if(!this.metrics){
37879             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37880         }
37881         var el = this.el;
37882         var v = el.dom.value;
37883         var d = document.createElement('div');
37884         d.appendChild(document.createTextNode(v));
37885         v = d.innerHTML;
37886         d = null;
37887         v += "&#160;";
37888         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37889         this.el.setWidth(w);
37890         this.fireEvent("autosize", this, w);
37891     },
37892     
37893     // private
37894     SafariOnKeyDown : function(event)
37895     {
37896         // this is a workaround for a password hang bug on chrome/ webkit.
37897         
37898         var isSelectAll = false;
37899         
37900         if(this.el.dom.selectionEnd > 0){
37901             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37902         }
37903         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37904             event.preventDefault();
37905             this.setValue('');
37906             return;
37907         }
37908         
37909         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37910             
37911             event.preventDefault();
37912             // this is very hacky as keydown always get's upper case.
37913             
37914             var cc = String.fromCharCode(event.getCharCode());
37915             
37916             
37917             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37918             
37919         }
37920         
37921         
37922     }
37923 });/*
37924  * Based on:
37925  * Ext JS Library 1.1.1
37926  * Copyright(c) 2006-2007, Ext JS, LLC.
37927  *
37928  * Originally Released Under LGPL - original licence link has changed is not relivant.
37929  *
37930  * Fork - LGPL
37931  * <script type="text/javascript">
37932  */
37933  
37934 /**
37935  * @class Roo.form.Hidden
37936  * @extends Roo.form.TextField
37937  * Simple Hidden element used on forms 
37938  * 
37939  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37940  * 
37941  * @constructor
37942  * Creates a new Hidden form element.
37943  * @param {Object} config Configuration options
37944  */
37945
37946
37947
37948 // easy hidden field...
37949 Roo.form.Hidden = function(config){
37950     Roo.form.Hidden.superclass.constructor.call(this, config);
37951 };
37952   
37953 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37954     fieldLabel:      '',
37955     inputType:      'hidden',
37956     width:          50,
37957     allowBlank:     true,
37958     labelSeparator: '',
37959     hidden:         true,
37960     itemCls :       'x-form-item-display-none'
37961
37962
37963 });
37964
37965
37966 /*
37967  * Based on:
37968  * Ext JS Library 1.1.1
37969  * Copyright(c) 2006-2007, Ext JS, LLC.
37970  *
37971  * Originally Released Under LGPL - original licence link has changed is not relivant.
37972  *
37973  * Fork - LGPL
37974  * <script type="text/javascript">
37975  */
37976  
37977 /**
37978  * @class Roo.form.TriggerField
37979  * @extends Roo.form.TextField
37980  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37981  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37982  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37983  * for which you can provide a custom implementation.  For example:
37984  * <pre><code>
37985 var trigger = new Roo.form.TriggerField();
37986 trigger.onTriggerClick = myTriggerFn;
37987 trigger.applyTo('my-field');
37988 </code></pre>
37989  *
37990  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37991  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37992  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37993  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37994  * @constructor
37995  * Create a new TriggerField.
37996  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37997  * to the base TextField)
37998  */
37999 Roo.form.TriggerField = function(config){
38000     this.mimicing = false;
38001     Roo.form.TriggerField.superclass.constructor.call(this, config);
38002 };
38003
38004 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
38005     /**
38006      * @cfg {String} triggerClass A CSS class to apply to the trigger
38007      */
38008     /**
38009      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38010      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38011      */
38012     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38013     /**
38014      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38015      */
38016     hideTrigger:false,
38017
38018     /** @cfg {Boolean} grow @hide */
38019     /** @cfg {Number} growMin @hide */
38020     /** @cfg {Number} growMax @hide */
38021
38022     /**
38023      * @hide 
38024      * @method
38025      */
38026     autoSize: Roo.emptyFn,
38027     // private
38028     monitorTab : true,
38029     // private
38030     deferHeight : true,
38031
38032     
38033     actionMode : 'wrap',
38034     // private
38035     onResize : function(w, h){
38036         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38037         if(typeof w == 'number'){
38038             var x = w - this.trigger.getWidth();
38039             this.el.setWidth(this.adjustWidth('input', x));
38040             this.trigger.setStyle('left', x+'px');
38041         }
38042     },
38043
38044     // private
38045     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38046
38047     // private
38048     getResizeEl : function(){
38049         return this.wrap;
38050     },
38051
38052     // private
38053     getPositionEl : function(){
38054         return this.wrap;
38055     },
38056
38057     // private
38058     alignErrorIcon : function(){
38059         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38060     },
38061
38062     // private
38063     onRender : function(ct, position){
38064         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38065         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38066         this.trigger = this.wrap.createChild(this.triggerConfig ||
38067                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38068         if(this.hideTrigger){
38069             this.trigger.setDisplayed(false);
38070         }
38071         this.initTrigger();
38072         if(!this.width){
38073             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38074         }
38075     },
38076
38077     // private
38078     initTrigger : function(){
38079         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38080         this.trigger.addClassOnOver('x-form-trigger-over');
38081         this.trigger.addClassOnClick('x-form-trigger-click');
38082     },
38083
38084     // private
38085     onDestroy : function(){
38086         if(this.trigger){
38087             this.trigger.removeAllListeners();
38088             this.trigger.remove();
38089         }
38090         if(this.wrap){
38091             this.wrap.remove();
38092         }
38093         Roo.form.TriggerField.superclass.onDestroy.call(this);
38094     },
38095
38096     // private
38097     onFocus : function(){
38098         Roo.form.TriggerField.superclass.onFocus.call(this);
38099         if(!this.mimicing){
38100             this.wrap.addClass('x-trigger-wrap-focus');
38101             this.mimicing = true;
38102             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38103             if(this.monitorTab){
38104                 this.el.on("keydown", this.checkTab, this);
38105             }
38106         }
38107     },
38108
38109     // private
38110     checkTab : function(e){
38111         if(e.getKey() == e.TAB){
38112             this.triggerBlur();
38113         }
38114     },
38115
38116     // private
38117     onBlur : function(){
38118         // do nothing
38119     },
38120
38121     // private
38122     mimicBlur : function(e, t){
38123         if(!this.wrap.contains(t) && this.validateBlur()){
38124             this.triggerBlur();
38125         }
38126     },
38127
38128     // private
38129     triggerBlur : function(){
38130         this.mimicing = false;
38131         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38132         if(this.monitorTab){
38133             this.el.un("keydown", this.checkTab, this);
38134         }
38135         this.wrap.removeClass('x-trigger-wrap-focus');
38136         Roo.form.TriggerField.superclass.onBlur.call(this);
38137     },
38138
38139     // private
38140     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38141     validateBlur : function(e, t){
38142         return true;
38143     },
38144
38145     // private
38146     onDisable : function(){
38147         Roo.form.TriggerField.superclass.onDisable.call(this);
38148         if(this.wrap){
38149             this.wrap.addClass('x-item-disabled');
38150         }
38151     },
38152
38153     // private
38154     onEnable : function(){
38155         Roo.form.TriggerField.superclass.onEnable.call(this);
38156         if(this.wrap){
38157             this.wrap.removeClass('x-item-disabled');
38158         }
38159     },
38160
38161     // private
38162     onShow : function(){
38163         var ae = this.getActionEl();
38164         
38165         if(ae){
38166             ae.dom.style.display = '';
38167             ae.dom.style.visibility = 'visible';
38168         }
38169     },
38170
38171     // private
38172     
38173     onHide : function(){
38174         var ae = this.getActionEl();
38175         ae.dom.style.display = 'none';
38176     },
38177
38178     /**
38179      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38180      * by an implementing function.
38181      * @method
38182      * @param {EventObject} e
38183      */
38184     onTriggerClick : Roo.emptyFn
38185 });
38186
38187 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38188 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38189 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38190 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38191     initComponent : function(){
38192         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38193
38194         this.triggerConfig = {
38195             tag:'span', cls:'x-form-twin-triggers', cn:[
38196             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38197             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38198         ]};
38199     },
38200
38201     getTrigger : function(index){
38202         return this.triggers[index];
38203     },
38204
38205     initTrigger : function(){
38206         var ts = this.trigger.select('.x-form-trigger', true);
38207         this.wrap.setStyle('overflow', 'hidden');
38208         var triggerField = this;
38209         ts.each(function(t, all, index){
38210             t.hide = function(){
38211                 var w = triggerField.wrap.getWidth();
38212                 this.dom.style.display = 'none';
38213                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38214             };
38215             t.show = function(){
38216                 var w = triggerField.wrap.getWidth();
38217                 this.dom.style.display = '';
38218                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38219             };
38220             var triggerIndex = 'Trigger'+(index+1);
38221
38222             if(this['hide'+triggerIndex]){
38223                 t.dom.style.display = 'none';
38224             }
38225             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38226             t.addClassOnOver('x-form-trigger-over');
38227             t.addClassOnClick('x-form-trigger-click');
38228         }, this);
38229         this.triggers = ts.elements;
38230     },
38231
38232     onTrigger1Click : Roo.emptyFn,
38233     onTrigger2Click : Roo.emptyFn
38234 });/*
38235  * Based on:
38236  * Ext JS Library 1.1.1
38237  * Copyright(c) 2006-2007, Ext JS, LLC.
38238  *
38239  * Originally Released Under LGPL - original licence link has changed is not relivant.
38240  *
38241  * Fork - LGPL
38242  * <script type="text/javascript">
38243  */
38244  
38245 /**
38246  * @class Roo.form.TextArea
38247  * @extends Roo.form.TextField
38248  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38249  * support for auto-sizing.
38250  * @constructor
38251  * Creates a new TextArea
38252  * @param {Object} config Configuration options
38253  */
38254 Roo.form.TextArea = function(config){
38255     Roo.form.TextArea.superclass.constructor.call(this, config);
38256     // these are provided exchanges for backwards compat
38257     // minHeight/maxHeight were replaced by growMin/growMax to be
38258     // compatible with TextField growing config values
38259     if(this.minHeight !== undefined){
38260         this.growMin = this.minHeight;
38261     }
38262     if(this.maxHeight !== undefined){
38263         this.growMax = this.maxHeight;
38264     }
38265 };
38266
38267 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38268     /**
38269      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38270      */
38271     growMin : 60,
38272     /**
38273      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38274      */
38275     growMax: 1000,
38276     /**
38277      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38278      * in the field (equivalent to setting overflow: hidden, defaults to false)
38279      */
38280     preventScrollbars: false,
38281     /**
38282      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38283      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38284      */
38285
38286     // private
38287     onRender : function(ct, position){
38288         if(!this.el){
38289             this.defaultAutoCreate = {
38290                 tag: "textarea",
38291                 style:"width:300px;height:60px;",
38292                 autocomplete: "new-password"
38293             };
38294         }
38295         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38296         if(this.grow){
38297             this.textSizeEl = Roo.DomHelper.append(document.body, {
38298                 tag: "pre", cls: "x-form-grow-sizer"
38299             });
38300             if(this.preventScrollbars){
38301                 this.el.setStyle("overflow", "hidden");
38302             }
38303             this.el.setHeight(this.growMin);
38304         }
38305     },
38306
38307     onDestroy : function(){
38308         if(this.textSizeEl){
38309             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38310         }
38311         Roo.form.TextArea.superclass.onDestroy.call(this);
38312     },
38313
38314     // private
38315     onKeyUp : function(e){
38316         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38317             this.autoSize();
38318         }
38319     },
38320
38321     /**
38322      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38323      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38324      */
38325     autoSize : function(){
38326         if(!this.grow || !this.textSizeEl){
38327             return;
38328         }
38329         var el = this.el;
38330         var v = el.dom.value;
38331         var ts = this.textSizeEl;
38332
38333         ts.innerHTML = '';
38334         ts.appendChild(document.createTextNode(v));
38335         v = ts.innerHTML;
38336
38337         Roo.fly(ts).setWidth(this.el.getWidth());
38338         if(v.length < 1){
38339             v = "&#160;&#160;";
38340         }else{
38341             if(Roo.isIE){
38342                 v = v.replace(/\n/g, '<p>&#160;</p>');
38343             }
38344             v += "&#160;\n&#160;";
38345         }
38346         ts.innerHTML = v;
38347         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38348         if(h != this.lastHeight){
38349             this.lastHeight = h;
38350             this.el.setHeight(h);
38351             this.fireEvent("autosize", this, h);
38352         }
38353     }
38354 });/*
38355  * Based on:
38356  * Ext JS Library 1.1.1
38357  * Copyright(c) 2006-2007, Ext JS, LLC.
38358  *
38359  * Originally Released Under LGPL - original licence link has changed is not relivant.
38360  *
38361  * Fork - LGPL
38362  * <script type="text/javascript">
38363  */
38364  
38365
38366 /**
38367  * @class Roo.form.NumberField
38368  * @extends Roo.form.TextField
38369  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38370  * @constructor
38371  * Creates a new NumberField
38372  * @param {Object} config Configuration options
38373  */
38374 Roo.form.NumberField = function(config){
38375     Roo.form.NumberField.superclass.constructor.call(this, config);
38376 };
38377
38378 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38379     /**
38380      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38381      */
38382     fieldClass: "x-form-field x-form-num-field",
38383     /**
38384      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38385      */
38386     allowDecimals : true,
38387     /**
38388      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38389      */
38390     decimalSeparator : ".",
38391     /**
38392      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38393      */
38394     decimalPrecision : 2,
38395     /**
38396      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38397      */
38398     allowNegative : true,
38399     /**
38400      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38401      */
38402     minValue : Number.NEGATIVE_INFINITY,
38403     /**
38404      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38405      */
38406     maxValue : Number.MAX_VALUE,
38407     /**
38408      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38409      */
38410     minText : "The minimum value for this field is {0}",
38411     /**
38412      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38413      */
38414     maxText : "The maximum value for this field is {0}",
38415     /**
38416      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38417      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38418      */
38419     nanText : "{0} is not a valid number",
38420
38421     // private
38422     initEvents : function(){
38423         Roo.form.NumberField.superclass.initEvents.call(this);
38424         var allowed = "0123456789";
38425         if(this.allowDecimals){
38426             allowed += this.decimalSeparator;
38427         }
38428         if(this.allowNegative){
38429             allowed += "-";
38430         }
38431         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38432         var keyPress = function(e){
38433             var k = e.getKey();
38434             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38435                 return;
38436             }
38437             var c = e.getCharCode();
38438             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38439                 e.stopEvent();
38440             }
38441         };
38442         this.el.on("keypress", keyPress, this);
38443     },
38444
38445     // private
38446     validateValue : function(value){
38447         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38448             return false;
38449         }
38450         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38451              return true;
38452         }
38453         var num = this.parseValue(value);
38454         if(isNaN(num)){
38455             this.markInvalid(String.format(this.nanText, value));
38456             return false;
38457         }
38458         if(num < this.minValue){
38459             this.markInvalid(String.format(this.minText, this.minValue));
38460             return false;
38461         }
38462         if(num > this.maxValue){
38463             this.markInvalid(String.format(this.maxText, this.maxValue));
38464             return false;
38465         }
38466         return true;
38467     },
38468
38469     getValue : function(){
38470         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38471     },
38472
38473     // private
38474     parseValue : function(value){
38475         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38476         return isNaN(value) ? '' : value;
38477     },
38478
38479     // private
38480     fixPrecision : function(value){
38481         var nan = isNaN(value);
38482         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38483             return nan ? '' : value;
38484         }
38485         return parseFloat(value).toFixed(this.decimalPrecision);
38486     },
38487
38488     setValue : function(v){
38489         v = this.fixPrecision(v);
38490         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38491     },
38492
38493     // private
38494     decimalPrecisionFcn : function(v){
38495         return Math.floor(v);
38496     },
38497
38498     beforeBlur : function(){
38499         var v = this.parseValue(this.getRawValue());
38500         if(v){
38501             this.setValue(v);
38502         }
38503     }
38504 });/*
38505  * Based on:
38506  * Ext JS Library 1.1.1
38507  * Copyright(c) 2006-2007, Ext JS, LLC.
38508  *
38509  * Originally Released Under LGPL - original licence link has changed is not relivant.
38510  *
38511  * Fork - LGPL
38512  * <script type="text/javascript">
38513  */
38514  
38515 /**
38516  * @class Roo.form.DateField
38517  * @extends Roo.form.TriggerField
38518  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38519 * @constructor
38520 * Create a new DateField
38521 * @param {Object} config
38522  */
38523 Roo.form.DateField = function(config){
38524     Roo.form.DateField.superclass.constructor.call(this, config);
38525     
38526       this.addEvents({
38527          
38528         /**
38529          * @event select
38530          * Fires when a date is selected
38531              * @param {Roo.form.DateField} combo This combo box
38532              * @param {Date} date The date selected
38533              */
38534         'select' : true
38535          
38536     });
38537     
38538     
38539     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38540     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38541     this.ddMatch = null;
38542     if(this.disabledDates){
38543         var dd = this.disabledDates;
38544         var re = "(?:";
38545         for(var i = 0; i < dd.length; i++){
38546             re += dd[i];
38547             if(i != dd.length-1) re += "|";
38548         }
38549         this.ddMatch = new RegExp(re + ")");
38550     }
38551 };
38552
38553 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38554     /**
38555      * @cfg {String} format
38556      * The default date format string which can be overriden for localization support.  The format must be
38557      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38558      */
38559     format : "m/d/y",
38560     /**
38561      * @cfg {String} altFormats
38562      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38563      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38564      */
38565     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38566     /**
38567      * @cfg {Array} disabledDays
38568      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38569      */
38570     disabledDays : null,
38571     /**
38572      * @cfg {String} disabledDaysText
38573      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38574      */
38575     disabledDaysText : "Disabled",
38576     /**
38577      * @cfg {Array} disabledDates
38578      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38579      * expression so they are very powerful. Some examples:
38580      * <ul>
38581      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38582      * <li>["03/08", "09/16"] would disable those days for every year</li>
38583      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38584      * <li>["03/../2006"] would disable every day in March 2006</li>
38585      * <li>["^03"] would disable every day in every March</li>
38586      * </ul>
38587      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38588      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38589      */
38590     disabledDates : null,
38591     /**
38592      * @cfg {String} disabledDatesText
38593      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38594      */
38595     disabledDatesText : "Disabled",
38596     /**
38597      * @cfg {Date/String} minValue
38598      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38599      * valid format (defaults to null).
38600      */
38601     minValue : null,
38602     /**
38603      * @cfg {Date/String} maxValue
38604      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38605      * valid format (defaults to null).
38606      */
38607     maxValue : null,
38608     /**
38609      * @cfg {String} minText
38610      * The error text to display when the date in the cell is before minValue (defaults to
38611      * 'The date in this field must be after {minValue}').
38612      */
38613     minText : "The date in this field must be equal to or after {0}",
38614     /**
38615      * @cfg {String} maxText
38616      * The error text to display when the date in the cell is after maxValue (defaults to
38617      * 'The date in this field must be before {maxValue}').
38618      */
38619     maxText : "The date in this field must be equal to or before {0}",
38620     /**
38621      * @cfg {String} invalidText
38622      * The error text to display when the date in the field is invalid (defaults to
38623      * '{value} is not a valid date - it must be in the format {format}').
38624      */
38625     invalidText : "{0} is not a valid date - it must be in the format {1}",
38626     /**
38627      * @cfg {String} triggerClass
38628      * An additional CSS class used to style the trigger button.  The trigger will always get the
38629      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38630      * which displays a calendar icon).
38631      */
38632     triggerClass : 'x-form-date-trigger',
38633     
38634
38635     /**
38636      * @cfg {Boolean} useIso
38637      * if enabled, then the date field will use a hidden field to store the 
38638      * real value as iso formated date. default (false)
38639      */ 
38640     useIso : false,
38641     /**
38642      * @cfg {String/Object} autoCreate
38643      * A DomHelper element spec, or true for a default element spec (defaults to
38644      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38645      */ 
38646     // private
38647     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38648     
38649     // private
38650     hiddenField: false,
38651     
38652     onRender : function(ct, position)
38653     {
38654         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38655         if (this.useIso) {
38656             //this.el.dom.removeAttribute('name'); 
38657             Roo.log("Changing name?");
38658             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38659             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38660                     'before', true);
38661             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38662             // prevent input submission
38663             this.hiddenName = this.name;
38664         }
38665             
38666             
38667     },
38668     
38669     // private
38670     validateValue : function(value)
38671     {
38672         value = this.formatDate(value);
38673         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38674             Roo.log('super failed');
38675             return false;
38676         }
38677         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38678              return true;
38679         }
38680         var svalue = value;
38681         value = this.parseDate(value);
38682         if(!value){
38683             Roo.log('parse date failed' + svalue);
38684             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38685             return false;
38686         }
38687         var time = value.getTime();
38688         if(this.minValue && time < this.minValue.getTime()){
38689             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38690             return false;
38691         }
38692         if(this.maxValue && time > this.maxValue.getTime()){
38693             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38694             return false;
38695         }
38696         if(this.disabledDays){
38697             var day = value.getDay();
38698             for(var i = 0; i < this.disabledDays.length; i++) {
38699                 if(day === this.disabledDays[i]){
38700                     this.markInvalid(this.disabledDaysText);
38701                     return false;
38702                 }
38703             }
38704         }
38705         var fvalue = this.formatDate(value);
38706         if(this.ddMatch && this.ddMatch.test(fvalue)){
38707             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38708             return false;
38709         }
38710         return true;
38711     },
38712
38713     // private
38714     // Provides logic to override the default TriggerField.validateBlur which just returns true
38715     validateBlur : function(){
38716         return !this.menu || !this.menu.isVisible();
38717     },
38718     
38719     getName: function()
38720     {
38721         // returns hidden if it's set..
38722         if (!this.rendered) {return ''};
38723         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38724         
38725     },
38726
38727     /**
38728      * Returns the current date value of the date field.
38729      * @return {Date} The date value
38730      */
38731     getValue : function(){
38732         
38733         return  this.hiddenField ?
38734                 this.hiddenField.value :
38735                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38736     },
38737
38738     /**
38739      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38740      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38741      * (the default format used is "m/d/y").
38742      * <br />Usage:
38743      * <pre><code>
38744 //All of these calls set the same date value (May 4, 2006)
38745
38746 //Pass a date object:
38747 var dt = new Date('5/4/06');
38748 dateField.setValue(dt);
38749
38750 //Pass a date string (default format):
38751 dateField.setValue('5/4/06');
38752
38753 //Pass a date string (custom format):
38754 dateField.format = 'Y-m-d';
38755 dateField.setValue('2006-5-4');
38756 </code></pre>
38757      * @param {String/Date} date The date or valid date string
38758      */
38759     setValue : function(date){
38760         if (this.hiddenField) {
38761             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38762         }
38763         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38764         // make sure the value field is always stored as a date..
38765         this.value = this.parseDate(date);
38766         
38767         
38768     },
38769
38770     // private
38771     parseDate : function(value){
38772         if(!value || value instanceof Date){
38773             return value;
38774         }
38775         var v = Date.parseDate(value, this.format);
38776          if (!v && this.useIso) {
38777             v = Date.parseDate(value, 'Y-m-d');
38778         }
38779         if(!v && this.altFormats){
38780             if(!this.altFormatsArray){
38781                 this.altFormatsArray = this.altFormats.split("|");
38782             }
38783             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38784                 v = Date.parseDate(value, this.altFormatsArray[i]);
38785             }
38786         }
38787         return v;
38788     },
38789
38790     // private
38791     formatDate : function(date, fmt){
38792         return (!date || !(date instanceof Date)) ?
38793                date : date.dateFormat(fmt || this.format);
38794     },
38795
38796     // private
38797     menuListeners : {
38798         select: function(m, d){
38799             
38800             this.setValue(d);
38801             this.fireEvent('select', this, d);
38802         },
38803         show : function(){ // retain focus styling
38804             this.onFocus();
38805         },
38806         hide : function(){
38807             this.focus.defer(10, this);
38808             var ml = this.menuListeners;
38809             this.menu.un("select", ml.select,  this);
38810             this.menu.un("show", ml.show,  this);
38811             this.menu.un("hide", ml.hide,  this);
38812         }
38813     },
38814
38815     // private
38816     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38817     onTriggerClick : function(){
38818         if(this.disabled){
38819             return;
38820         }
38821         if(this.menu == null){
38822             this.menu = new Roo.menu.DateMenu();
38823         }
38824         Roo.apply(this.menu.picker,  {
38825             showClear: this.allowBlank,
38826             minDate : this.minValue,
38827             maxDate : this.maxValue,
38828             disabledDatesRE : this.ddMatch,
38829             disabledDatesText : this.disabledDatesText,
38830             disabledDays : this.disabledDays,
38831             disabledDaysText : this.disabledDaysText,
38832             format : this.useIso ? 'Y-m-d' : this.format,
38833             minText : String.format(this.minText, this.formatDate(this.minValue)),
38834             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38835         });
38836         this.menu.on(Roo.apply({}, this.menuListeners, {
38837             scope:this
38838         }));
38839         this.menu.picker.setValue(this.getValue() || new Date());
38840         this.menu.show(this.el, "tl-bl?");
38841     },
38842
38843     beforeBlur : function(){
38844         var v = this.parseDate(this.getRawValue());
38845         if(v){
38846             this.setValue(v);
38847         }
38848     },
38849
38850     /*@
38851      * overide
38852      * 
38853      */
38854     isDirty : function() {
38855         if(this.disabled) {
38856             return false;
38857         }
38858         
38859         if(typeof(this.startValue) === 'undefined'){
38860             return false;
38861         }
38862         
38863         return String(this.getValue()) !== String(this.startValue);
38864         
38865     }
38866 });/*
38867  * Based on:
38868  * Ext JS Library 1.1.1
38869  * Copyright(c) 2006-2007, Ext JS, LLC.
38870  *
38871  * Originally Released Under LGPL - original licence link has changed is not relivant.
38872  *
38873  * Fork - LGPL
38874  * <script type="text/javascript">
38875  */
38876  
38877 /**
38878  * @class Roo.form.MonthField
38879  * @extends Roo.form.TriggerField
38880  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38881 * @constructor
38882 * Create a new MonthField
38883 * @param {Object} config
38884  */
38885 Roo.form.MonthField = function(config){
38886     
38887     Roo.form.MonthField.superclass.constructor.call(this, config);
38888     
38889       this.addEvents({
38890          
38891         /**
38892          * @event select
38893          * Fires when a date is selected
38894              * @param {Roo.form.MonthFieeld} combo This combo box
38895              * @param {Date} date The date selected
38896              */
38897         'select' : true
38898          
38899     });
38900     
38901     
38902     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38903     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38904     this.ddMatch = null;
38905     if(this.disabledDates){
38906         var dd = this.disabledDates;
38907         var re = "(?:";
38908         for(var i = 0; i < dd.length; i++){
38909             re += dd[i];
38910             if(i != dd.length-1) re += "|";
38911         }
38912         this.ddMatch = new RegExp(re + ")");
38913     }
38914 };
38915
38916 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38917     /**
38918      * @cfg {String} format
38919      * The default date format string which can be overriden for localization support.  The format must be
38920      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38921      */
38922     format : "M Y",
38923     /**
38924      * @cfg {String} altFormats
38925      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38926      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38927      */
38928     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38929     /**
38930      * @cfg {Array} disabledDays
38931      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38932      */
38933     disabledDays : [0,1,2,3,4,5,6],
38934     /**
38935      * @cfg {String} disabledDaysText
38936      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38937      */
38938     disabledDaysText : "Disabled",
38939     /**
38940      * @cfg {Array} disabledDates
38941      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38942      * expression so they are very powerful. Some examples:
38943      * <ul>
38944      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38945      * <li>["03/08", "09/16"] would disable those days for every year</li>
38946      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38947      * <li>["03/../2006"] would disable every day in March 2006</li>
38948      * <li>["^03"] would disable every day in every March</li>
38949      * </ul>
38950      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38951      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38952      */
38953     disabledDates : null,
38954     /**
38955      * @cfg {String} disabledDatesText
38956      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38957      */
38958     disabledDatesText : "Disabled",
38959     /**
38960      * @cfg {Date/String} minValue
38961      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38962      * valid format (defaults to null).
38963      */
38964     minValue : null,
38965     /**
38966      * @cfg {Date/String} maxValue
38967      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38968      * valid format (defaults to null).
38969      */
38970     maxValue : null,
38971     /**
38972      * @cfg {String} minText
38973      * The error text to display when the date in the cell is before minValue (defaults to
38974      * 'The date in this field must be after {minValue}').
38975      */
38976     minText : "The date in this field must be equal to or after {0}",
38977     /**
38978      * @cfg {String} maxTextf
38979      * The error text to display when the date in the cell is after maxValue (defaults to
38980      * 'The date in this field must be before {maxValue}').
38981      */
38982     maxText : "The date in this field must be equal to or before {0}",
38983     /**
38984      * @cfg {String} invalidText
38985      * The error text to display when the date in the field is invalid (defaults to
38986      * '{value} is not a valid date - it must be in the format {format}').
38987      */
38988     invalidText : "{0} is not a valid date - it must be in the format {1}",
38989     /**
38990      * @cfg {String} triggerClass
38991      * An additional CSS class used to style the trigger button.  The trigger will always get the
38992      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38993      * which displays a calendar icon).
38994      */
38995     triggerClass : 'x-form-date-trigger',
38996     
38997
38998     /**
38999      * @cfg {Boolean} useIso
39000      * if enabled, then the date field will use a hidden field to store the 
39001      * real value as iso formated date. default (true)
39002      */ 
39003     useIso : true,
39004     /**
39005      * @cfg {String/Object} autoCreate
39006      * A DomHelper element spec, or true for a default element spec (defaults to
39007      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39008      */ 
39009     // private
39010     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39011     
39012     // private
39013     hiddenField: false,
39014     
39015     hideMonthPicker : false,
39016     
39017     onRender : function(ct, position)
39018     {
39019         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39020         if (this.useIso) {
39021             this.el.dom.removeAttribute('name'); 
39022             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39023                     'before', true);
39024             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39025             // prevent input submission
39026             this.hiddenName = this.name;
39027         }
39028             
39029             
39030     },
39031     
39032     // private
39033     validateValue : function(value)
39034     {
39035         value = this.formatDate(value);
39036         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39037             return false;
39038         }
39039         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39040              return true;
39041         }
39042         var svalue = value;
39043         value = this.parseDate(value);
39044         if(!value){
39045             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39046             return false;
39047         }
39048         var time = value.getTime();
39049         if(this.minValue && time < this.minValue.getTime()){
39050             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39051             return false;
39052         }
39053         if(this.maxValue && time > this.maxValue.getTime()){
39054             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39055             return false;
39056         }
39057         /*if(this.disabledDays){
39058             var day = value.getDay();
39059             for(var i = 0; i < this.disabledDays.length; i++) {
39060                 if(day === this.disabledDays[i]){
39061                     this.markInvalid(this.disabledDaysText);
39062                     return false;
39063                 }
39064             }
39065         }
39066         */
39067         var fvalue = this.formatDate(value);
39068         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39069             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39070             return false;
39071         }
39072         */
39073         return true;
39074     },
39075
39076     // private
39077     // Provides logic to override the default TriggerField.validateBlur which just returns true
39078     validateBlur : function(){
39079         return !this.menu || !this.menu.isVisible();
39080     },
39081
39082     /**
39083      * Returns the current date value of the date field.
39084      * @return {Date} The date value
39085      */
39086     getValue : function(){
39087         
39088         
39089         
39090         return  this.hiddenField ?
39091                 this.hiddenField.value :
39092                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39093     },
39094
39095     /**
39096      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39097      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39098      * (the default format used is "m/d/y").
39099      * <br />Usage:
39100      * <pre><code>
39101 //All of these calls set the same date value (May 4, 2006)
39102
39103 //Pass a date object:
39104 var dt = new Date('5/4/06');
39105 monthField.setValue(dt);
39106
39107 //Pass a date string (default format):
39108 monthField.setValue('5/4/06');
39109
39110 //Pass a date string (custom format):
39111 monthField.format = 'Y-m-d';
39112 monthField.setValue('2006-5-4');
39113 </code></pre>
39114      * @param {String/Date} date The date or valid date string
39115      */
39116     setValue : function(date){
39117         Roo.log('month setValue' + date);
39118         // can only be first of month..
39119         
39120         var val = this.parseDate(date);
39121         
39122         if (this.hiddenField) {
39123             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39124         }
39125         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39126         this.value = this.parseDate(date);
39127     },
39128
39129     // private
39130     parseDate : function(value){
39131         if(!value || value instanceof Date){
39132             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39133             return value;
39134         }
39135         var v = Date.parseDate(value, this.format);
39136         if (!v && this.useIso) {
39137             v = Date.parseDate(value, 'Y-m-d');
39138         }
39139         if (v) {
39140             // 
39141             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39142         }
39143         
39144         
39145         if(!v && this.altFormats){
39146             if(!this.altFormatsArray){
39147                 this.altFormatsArray = this.altFormats.split("|");
39148             }
39149             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39150                 v = Date.parseDate(value, this.altFormatsArray[i]);
39151             }
39152         }
39153         return v;
39154     },
39155
39156     // private
39157     formatDate : function(date, fmt){
39158         return (!date || !(date instanceof Date)) ?
39159                date : date.dateFormat(fmt || this.format);
39160     },
39161
39162     // private
39163     menuListeners : {
39164         select: function(m, d){
39165             this.setValue(d);
39166             this.fireEvent('select', this, d);
39167         },
39168         show : function(){ // retain focus styling
39169             this.onFocus();
39170         },
39171         hide : function(){
39172             this.focus.defer(10, this);
39173             var ml = this.menuListeners;
39174             this.menu.un("select", ml.select,  this);
39175             this.menu.un("show", ml.show,  this);
39176             this.menu.un("hide", ml.hide,  this);
39177         }
39178     },
39179     // private
39180     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39181     onTriggerClick : function(){
39182         if(this.disabled){
39183             return;
39184         }
39185         if(this.menu == null){
39186             this.menu = new Roo.menu.DateMenu();
39187            
39188         }
39189         
39190         Roo.apply(this.menu.picker,  {
39191             
39192             showClear: this.allowBlank,
39193             minDate : this.minValue,
39194             maxDate : this.maxValue,
39195             disabledDatesRE : this.ddMatch,
39196             disabledDatesText : this.disabledDatesText,
39197             
39198             format : this.useIso ? 'Y-m-d' : this.format,
39199             minText : String.format(this.minText, this.formatDate(this.minValue)),
39200             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39201             
39202         });
39203          this.menu.on(Roo.apply({}, this.menuListeners, {
39204             scope:this
39205         }));
39206        
39207         
39208         var m = this.menu;
39209         var p = m.picker;
39210         
39211         // hide month picker get's called when we called by 'before hide';
39212         
39213         var ignorehide = true;
39214         p.hideMonthPicker  = function(disableAnim){
39215             if (ignorehide) {
39216                 return;
39217             }
39218              if(this.monthPicker){
39219                 Roo.log("hideMonthPicker called");
39220                 if(disableAnim === true){
39221                     this.monthPicker.hide();
39222                 }else{
39223                     this.monthPicker.slideOut('t', {duration:.2});
39224                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39225                     p.fireEvent("select", this, this.value);
39226                     m.hide();
39227                 }
39228             }
39229         }
39230         
39231         Roo.log('picker set value');
39232         Roo.log(this.getValue());
39233         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39234         m.show(this.el, 'tl-bl?');
39235         ignorehide  = false;
39236         // this will trigger hideMonthPicker..
39237         
39238         
39239         // hidden the day picker
39240         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39241         
39242         
39243         
39244       
39245         
39246         p.showMonthPicker.defer(100, p);
39247     
39248         
39249        
39250     },
39251
39252     beforeBlur : function(){
39253         var v = this.parseDate(this.getRawValue());
39254         if(v){
39255             this.setValue(v);
39256         }
39257     }
39258
39259     /** @cfg {Boolean} grow @hide */
39260     /** @cfg {Number} growMin @hide */
39261     /** @cfg {Number} growMax @hide */
39262     /**
39263      * @hide
39264      * @method autoSize
39265      */
39266 });/*
39267  * Based on:
39268  * Ext JS Library 1.1.1
39269  * Copyright(c) 2006-2007, Ext JS, LLC.
39270  *
39271  * Originally Released Under LGPL - original licence link has changed is not relivant.
39272  *
39273  * Fork - LGPL
39274  * <script type="text/javascript">
39275  */
39276  
39277
39278 /**
39279  * @class Roo.form.ComboBox
39280  * @extends Roo.form.TriggerField
39281  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39282  * @constructor
39283  * Create a new ComboBox.
39284  * @param {Object} config Configuration options
39285  */
39286 Roo.form.ComboBox = function(config){
39287     Roo.form.ComboBox.superclass.constructor.call(this, config);
39288     this.addEvents({
39289         /**
39290          * @event expand
39291          * Fires when the dropdown list is expanded
39292              * @param {Roo.form.ComboBox} combo This combo box
39293              */
39294         'expand' : true,
39295         /**
39296          * @event collapse
39297          * Fires when the dropdown list is collapsed
39298              * @param {Roo.form.ComboBox} combo This combo box
39299              */
39300         'collapse' : true,
39301         /**
39302          * @event beforeselect
39303          * Fires before a list item is selected. Return false to cancel the selection.
39304              * @param {Roo.form.ComboBox} combo This combo box
39305              * @param {Roo.data.Record} record The data record returned from the underlying store
39306              * @param {Number} index The index of the selected item in the dropdown list
39307              */
39308         'beforeselect' : true,
39309         /**
39310          * @event select
39311          * Fires when a list item is selected
39312              * @param {Roo.form.ComboBox} combo This combo box
39313              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39314              * @param {Number} index The index of the selected item in the dropdown list
39315              */
39316         'select' : true,
39317         /**
39318          * @event beforequery
39319          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39320          * The event object passed has these properties:
39321              * @param {Roo.form.ComboBox} combo This combo box
39322              * @param {String} query The query
39323              * @param {Boolean} forceAll true to force "all" query
39324              * @param {Boolean} cancel true to cancel the query
39325              * @param {Object} e The query event object
39326              */
39327         'beforequery': true,
39328          /**
39329          * @event add
39330          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39331              * @param {Roo.form.ComboBox} combo This combo box
39332              */
39333         'add' : true,
39334         /**
39335          * @event edit
39336          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39337              * @param {Roo.form.ComboBox} combo This combo box
39338              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39339              */
39340         'edit' : true
39341         
39342         
39343     });
39344     if(this.transform){
39345         this.allowDomMove = false;
39346         var s = Roo.getDom(this.transform);
39347         if(!this.hiddenName){
39348             this.hiddenName = s.name;
39349         }
39350         if(!this.store){
39351             this.mode = 'local';
39352             var d = [], opts = s.options;
39353             for(var i = 0, len = opts.length;i < len; i++){
39354                 var o = opts[i];
39355                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39356                 if(o.selected) {
39357                     this.value = value;
39358                 }
39359                 d.push([value, o.text]);
39360             }
39361             this.store = new Roo.data.SimpleStore({
39362                 'id': 0,
39363                 fields: ['value', 'text'],
39364                 data : d
39365             });
39366             this.valueField = 'value';
39367             this.displayField = 'text';
39368         }
39369         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39370         if(!this.lazyRender){
39371             this.target = true;
39372             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39373             s.parentNode.removeChild(s); // remove it
39374             this.render(this.el.parentNode);
39375         }else{
39376             s.parentNode.removeChild(s); // remove it
39377         }
39378
39379     }
39380     if (this.store) {
39381         this.store = Roo.factory(this.store, Roo.data);
39382     }
39383     
39384     this.selectedIndex = -1;
39385     if(this.mode == 'local'){
39386         if(config.queryDelay === undefined){
39387             this.queryDelay = 10;
39388         }
39389         if(config.minChars === undefined){
39390             this.minChars = 0;
39391         }
39392     }
39393 };
39394
39395 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39396     /**
39397      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39398      */
39399     /**
39400      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39401      * rendering into an Roo.Editor, defaults to false)
39402      */
39403     /**
39404      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39405      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39406      */
39407     /**
39408      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39409      */
39410     /**
39411      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39412      * the dropdown list (defaults to undefined, with no header element)
39413      */
39414
39415      /**
39416      * @cfg {String/Roo.Template} tpl The template to use to render the output
39417      */
39418      
39419     // private
39420     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39421     /**
39422      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39423      */
39424     listWidth: undefined,
39425     /**
39426      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39427      * mode = 'remote' or 'text' if mode = 'local')
39428      */
39429     displayField: undefined,
39430     /**
39431      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39432      * mode = 'remote' or 'value' if mode = 'local'). 
39433      * Note: use of a valueField requires the user make a selection
39434      * in order for a value to be mapped.
39435      */
39436     valueField: undefined,
39437     
39438     
39439     /**
39440      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39441      * field's data value (defaults to the underlying DOM element's name)
39442      */
39443     hiddenName: undefined,
39444     /**
39445      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39446      */
39447     listClass: '',
39448     /**
39449      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39450      */
39451     selectedClass: 'x-combo-selected',
39452     /**
39453      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39454      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39455      * which displays a downward arrow icon).
39456      */
39457     triggerClass : 'x-form-arrow-trigger',
39458     /**
39459      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39460      */
39461     shadow:'sides',
39462     /**
39463      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39464      * anchor positions (defaults to 'tl-bl')
39465      */
39466     listAlign: 'tl-bl?',
39467     /**
39468      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39469      */
39470     maxHeight: 300,
39471     /**
39472      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39473      * query specified by the allQuery config option (defaults to 'query')
39474      */
39475     triggerAction: 'query',
39476     /**
39477      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39478      * (defaults to 4, does not apply if editable = false)
39479      */
39480     minChars : 4,
39481     /**
39482      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39483      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39484      */
39485     typeAhead: false,
39486     /**
39487      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39488      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39489      */
39490     queryDelay: 500,
39491     /**
39492      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39493      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39494      */
39495     pageSize: 0,
39496     /**
39497      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39498      * when editable = true (defaults to false)
39499      */
39500     selectOnFocus:false,
39501     /**
39502      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39503      */
39504     queryParam: 'query',
39505     /**
39506      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39507      * when mode = 'remote' (defaults to 'Loading...')
39508      */
39509     loadingText: 'Loading...',
39510     /**
39511      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39512      */
39513     resizable: false,
39514     /**
39515      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39516      */
39517     handleHeight : 8,
39518     /**
39519      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39520      * traditional select (defaults to true)
39521      */
39522     editable: true,
39523     /**
39524      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39525      */
39526     allQuery: '',
39527     /**
39528      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39529      */
39530     mode: 'remote',
39531     /**
39532      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39533      * listWidth has a higher value)
39534      */
39535     minListWidth : 70,
39536     /**
39537      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39538      * allow the user to set arbitrary text into the field (defaults to false)
39539      */
39540     forceSelection:false,
39541     /**
39542      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39543      * if typeAhead = true (defaults to 250)
39544      */
39545     typeAheadDelay : 250,
39546     /**
39547      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39548      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39549      */
39550     valueNotFoundText : undefined,
39551     /**
39552      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39553      */
39554     blockFocus : false,
39555     
39556     /**
39557      * @cfg {Boolean} disableClear Disable showing of clear button.
39558      */
39559     disableClear : false,
39560     /**
39561      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39562      */
39563     alwaysQuery : false,
39564     
39565     //private
39566     addicon : false,
39567     editicon: false,
39568     
39569     // element that contains real text value.. (when hidden is used..)
39570      
39571     // private
39572     onRender : function(ct, position){
39573         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39574         if(this.hiddenName){
39575             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39576                     'before', true);
39577             this.hiddenField.value =
39578                 this.hiddenValue !== undefined ? this.hiddenValue :
39579                 this.value !== undefined ? this.value : '';
39580
39581             // prevent input submission
39582             this.el.dom.removeAttribute('name');
39583              
39584              
39585         }
39586         if(Roo.isGecko){
39587             this.el.dom.setAttribute('autocomplete', 'off');
39588         }
39589
39590         var cls = 'x-combo-list';
39591
39592         this.list = new Roo.Layer({
39593             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39594         });
39595
39596         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39597         this.list.setWidth(lw);
39598         this.list.swallowEvent('mousewheel');
39599         this.assetHeight = 0;
39600
39601         if(this.title){
39602             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39603             this.assetHeight += this.header.getHeight();
39604         }
39605
39606         this.innerList = this.list.createChild({cls:cls+'-inner'});
39607         this.innerList.on('mouseover', this.onViewOver, this);
39608         this.innerList.on('mousemove', this.onViewMove, this);
39609         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39610         
39611         if(this.allowBlank && !this.pageSize && !this.disableClear){
39612             this.footer = this.list.createChild({cls:cls+'-ft'});
39613             this.pageTb = new Roo.Toolbar(this.footer);
39614            
39615         }
39616         if(this.pageSize){
39617             this.footer = this.list.createChild({cls:cls+'-ft'});
39618             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39619                     {pageSize: this.pageSize});
39620             
39621         }
39622         
39623         if (this.pageTb && this.allowBlank && !this.disableClear) {
39624             var _this = this;
39625             this.pageTb.add(new Roo.Toolbar.Fill(), {
39626                 cls: 'x-btn-icon x-btn-clear',
39627                 text: '&#160;',
39628                 handler: function()
39629                 {
39630                     _this.collapse();
39631                     _this.clearValue();
39632                     _this.onSelect(false, -1);
39633                 }
39634             });
39635         }
39636         if (this.footer) {
39637             this.assetHeight += this.footer.getHeight();
39638         }
39639         
39640
39641         if(!this.tpl){
39642             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39643         }
39644
39645         this.view = new Roo.View(this.innerList, this.tpl, {
39646             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39647         });
39648
39649         this.view.on('click', this.onViewClick, this);
39650
39651         this.store.on('beforeload', this.onBeforeLoad, this);
39652         this.store.on('load', this.onLoad, this);
39653         this.store.on('loadexception', this.onLoadException, this);
39654
39655         if(this.resizable){
39656             this.resizer = new Roo.Resizable(this.list,  {
39657                pinned:true, handles:'se'
39658             });
39659             this.resizer.on('resize', function(r, w, h){
39660                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39661                 this.listWidth = w;
39662                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39663                 this.restrictHeight();
39664             }, this);
39665             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39666         }
39667         if(!this.editable){
39668             this.editable = true;
39669             this.setEditable(false);
39670         }  
39671         
39672         
39673         if (typeof(this.events.add.listeners) != 'undefined') {
39674             
39675             this.addicon = this.wrap.createChild(
39676                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39677        
39678             this.addicon.on('click', function(e) {
39679                 this.fireEvent('add', this);
39680             }, this);
39681         }
39682         if (typeof(this.events.edit.listeners) != 'undefined') {
39683             
39684             this.editicon = this.wrap.createChild(
39685                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39686             if (this.addicon) {
39687                 this.editicon.setStyle('margin-left', '40px');
39688             }
39689             this.editicon.on('click', function(e) {
39690                 
39691                 // we fire even  if inothing is selected..
39692                 this.fireEvent('edit', this, this.lastData );
39693                 
39694             }, this);
39695         }
39696         
39697         
39698         
39699     },
39700
39701     // private
39702     initEvents : function(){
39703         Roo.form.ComboBox.superclass.initEvents.call(this);
39704
39705         this.keyNav = new Roo.KeyNav(this.el, {
39706             "up" : function(e){
39707                 this.inKeyMode = true;
39708                 this.selectPrev();
39709             },
39710
39711             "down" : function(e){
39712                 if(!this.isExpanded()){
39713                     this.onTriggerClick();
39714                 }else{
39715                     this.inKeyMode = true;
39716                     this.selectNext();
39717                 }
39718             },
39719
39720             "enter" : function(e){
39721                 this.onViewClick();
39722                 //return true;
39723             },
39724
39725             "esc" : function(e){
39726                 this.collapse();
39727             },
39728
39729             "tab" : function(e){
39730                 this.onViewClick(false);
39731                 this.fireEvent("specialkey", this, e);
39732                 return true;
39733             },
39734
39735             scope : this,
39736
39737             doRelay : function(foo, bar, hname){
39738                 if(hname == 'down' || this.scope.isExpanded()){
39739                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39740                 }
39741                 return true;
39742             },
39743
39744             forceKeyDown: true
39745         });
39746         this.queryDelay = Math.max(this.queryDelay || 10,
39747                 this.mode == 'local' ? 10 : 250);
39748         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39749         if(this.typeAhead){
39750             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39751         }
39752         if(this.editable !== false){
39753             this.el.on("keyup", this.onKeyUp, this);
39754         }
39755         if(this.forceSelection){
39756             this.on('blur', this.doForce, this);
39757         }
39758     },
39759
39760     onDestroy : function(){
39761         if(this.view){
39762             this.view.setStore(null);
39763             this.view.el.removeAllListeners();
39764             this.view.el.remove();
39765             this.view.purgeListeners();
39766         }
39767         if(this.list){
39768             this.list.destroy();
39769         }
39770         if(this.store){
39771             this.store.un('beforeload', this.onBeforeLoad, this);
39772             this.store.un('load', this.onLoad, this);
39773             this.store.un('loadexception', this.onLoadException, this);
39774         }
39775         Roo.form.ComboBox.superclass.onDestroy.call(this);
39776     },
39777
39778     // private
39779     fireKey : function(e){
39780         if(e.isNavKeyPress() && !this.list.isVisible()){
39781             this.fireEvent("specialkey", this, e);
39782         }
39783     },
39784
39785     // private
39786     onResize: function(w, h){
39787         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39788         
39789         if(typeof w != 'number'){
39790             // we do not handle it!?!?
39791             return;
39792         }
39793         var tw = this.trigger.getWidth();
39794         tw += this.addicon ? this.addicon.getWidth() : 0;
39795         tw += this.editicon ? this.editicon.getWidth() : 0;
39796         var x = w - tw;
39797         this.el.setWidth( this.adjustWidth('input', x));
39798             
39799         this.trigger.setStyle('left', x+'px');
39800         
39801         if(this.list && this.listWidth === undefined){
39802             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39803             this.list.setWidth(lw);
39804             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39805         }
39806         
39807     
39808         
39809     },
39810
39811     /**
39812      * Allow or prevent the user from directly editing the field text.  If false is passed,
39813      * the user will only be able to select from the items defined in the dropdown list.  This method
39814      * is the runtime equivalent of setting the 'editable' config option at config time.
39815      * @param {Boolean} value True to allow the user to directly edit the field text
39816      */
39817     setEditable : function(value){
39818         if(value == this.editable){
39819             return;
39820         }
39821         this.editable = value;
39822         if(!value){
39823             this.el.dom.setAttribute('readOnly', true);
39824             this.el.on('mousedown', this.onTriggerClick,  this);
39825             this.el.addClass('x-combo-noedit');
39826         }else{
39827             this.el.dom.setAttribute('readOnly', false);
39828             this.el.un('mousedown', this.onTriggerClick,  this);
39829             this.el.removeClass('x-combo-noedit');
39830         }
39831     },
39832
39833     // private
39834     onBeforeLoad : function(){
39835         if(!this.hasFocus){
39836             return;
39837         }
39838         this.innerList.update(this.loadingText ?
39839                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39840         this.restrictHeight();
39841         this.selectedIndex = -1;
39842     },
39843
39844     // private
39845     onLoad : function(){
39846         if(!this.hasFocus){
39847             return;
39848         }
39849         if(this.store.getCount() > 0){
39850             this.expand();
39851             this.restrictHeight();
39852             if(this.lastQuery == this.allQuery){
39853                 if(this.editable){
39854                     this.el.dom.select();
39855                 }
39856                 if(!this.selectByValue(this.value, true)){
39857                     this.select(0, true);
39858                 }
39859             }else{
39860                 this.selectNext();
39861                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39862                     this.taTask.delay(this.typeAheadDelay);
39863                 }
39864             }
39865         }else{
39866             this.onEmptyResults();
39867         }
39868         //this.el.focus();
39869     },
39870     // private
39871     onLoadException : function()
39872     {
39873         this.collapse();
39874         Roo.log(this.store.reader.jsonData);
39875         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39876             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39877         }
39878         
39879         
39880     },
39881     // private
39882     onTypeAhead : function(){
39883         if(this.store.getCount() > 0){
39884             var r = this.store.getAt(0);
39885             var newValue = r.data[this.displayField];
39886             var len = newValue.length;
39887             var selStart = this.getRawValue().length;
39888             if(selStart != len){
39889                 this.setRawValue(newValue);
39890                 this.selectText(selStart, newValue.length);
39891             }
39892         }
39893     },
39894
39895     // private
39896     onSelect : function(record, index){
39897         if(this.fireEvent('beforeselect', this, record, index) !== false){
39898             this.setFromData(index > -1 ? record.data : false);
39899             this.collapse();
39900             this.fireEvent('select', this, record, index);
39901         }
39902     },
39903
39904     /**
39905      * Returns the currently selected field value or empty string if no value is set.
39906      * @return {String} value The selected value
39907      */
39908     getValue : function(){
39909         if(this.valueField){
39910             return typeof this.value != 'undefined' ? this.value : '';
39911         }
39912         return Roo.form.ComboBox.superclass.getValue.call(this);
39913     },
39914
39915     /**
39916      * Clears any text/value currently set in the field
39917      */
39918     clearValue : function(){
39919         if(this.hiddenField){
39920             this.hiddenField.value = '';
39921         }
39922         this.value = '';
39923         this.setRawValue('');
39924         this.lastSelectionText = '';
39925         
39926     },
39927
39928     /**
39929      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39930      * will be displayed in the field.  If the value does not match the data value of an existing item,
39931      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39932      * Otherwise the field will be blank (although the value will still be set).
39933      * @param {String} value The value to match
39934      */
39935     setValue : function(v){
39936         var text = v;
39937         if(this.valueField){
39938             var r = this.findRecord(this.valueField, v);
39939             if(r){
39940                 text = r.data[this.displayField];
39941             }else if(this.valueNotFoundText !== undefined){
39942                 text = this.valueNotFoundText;
39943             }
39944         }
39945         this.lastSelectionText = text;
39946         if(this.hiddenField){
39947             this.hiddenField.value = v;
39948         }
39949         Roo.form.ComboBox.superclass.setValue.call(this, text);
39950         this.value = v;
39951     },
39952     /**
39953      * @property {Object} the last set data for the element
39954      */
39955     
39956     lastData : false,
39957     /**
39958      * Sets the value of the field based on a object which is related to the record format for the store.
39959      * @param {Object} value the value to set as. or false on reset?
39960      */
39961     setFromData : function(o){
39962         var dv = ''; // display value
39963         var vv = ''; // value value..
39964         this.lastData = o;
39965         if (this.displayField) {
39966             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39967         } else {
39968             // this is an error condition!!!
39969             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39970         }
39971         
39972         if(this.valueField){
39973             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39974         }
39975         if(this.hiddenField){
39976             this.hiddenField.value = vv;
39977             
39978             this.lastSelectionText = dv;
39979             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39980             this.value = vv;
39981             return;
39982         }
39983         // no hidden field.. - we store the value in 'value', but still display
39984         // display field!!!!
39985         this.lastSelectionText = dv;
39986         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39987         this.value = vv;
39988         
39989         
39990     },
39991     // private
39992     reset : function(){
39993         // overridden so that last data is reset..
39994         this.setValue(this.resetValue);
39995         this.clearInvalid();
39996         this.lastData = false;
39997         if (this.view) {
39998             this.view.clearSelections();
39999         }
40000     },
40001     // private
40002     findRecord : function(prop, value){
40003         var record;
40004         if(this.store.getCount() > 0){
40005             this.store.each(function(r){
40006                 if(r.data[prop] == value){
40007                     record = r;
40008                     return false;
40009                 }
40010                 return true;
40011             });
40012         }
40013         return record;
40014     },
40015     
40016     getName: function()
40017     {
40018         // returns hidden if it's set..
40019         if (!this.rendered) {return ''};
40020         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40021         
40022     },
40023     // private
40024     onViewMove : function(e, t){
40025         this.inKeyMode = false;
40026     },
40027
40028     // private
40029     onViewOver : function(e, t){
40030         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40031             return;
40032         }
40033         var item = this.view.findItemFromChild(t);
40034         if(item){
40035             var index = this.view.indexOf(item);
40036             this.select(index, false);
40037         }
40038     },
40039
40040     // private
40041     onViewClick : function(doFocus)
40042     {
40043         var index = this.view.getSelectedIndexes()[0];
40044         var r = this.store.getAt(index);
40045         if(r){
40046             this.onSelect(r, index);
40047         }
40048         if(doFocus !== false && !this.blockFocus){
40049             this.el.focus();
40050         }
40051     },
40052
40053     // private
40054     restrictHeight : function(){
40055         this.innerList.dom.style.height = '';
40056         var inner = this.innerList.dom;
40057         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40058         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40059         this.list.beginUpdate();
40060         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40061         this.list.alignTo(this.el, this.listAlign);
40062         this.list.endUpdate();
40063     },
40064
40065     // private
40066     onEmptyResults : function(){
40067         this.collapse();
40068     },
40069
40070     /**
40071      * Returns true if the dropdown list is expanded, else false.
40072      */
40073     isExpanded : function(){
40074         return this.list.isVisible();
40075     },
40076
40077     /**
40078      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40079      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40080      * @param {String} value The data value of the item to select
40081      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40082      * selected item if it is not currently in view (defaults to true)
40083      * @return {Boolean} True if the value matched an item in the list, else false
40084      */
40085     selectByValue : function(v, scrollIntoView){
40086         if(v !== undefined && v !== null){
40087             var r = this.findRecord(this.valueField || this.displayField, v);
40088             if(r){
40089                 this.select(this.store.indexOf(r), scrollIntoView);
40090                 return true;
40091             }
40092         }
40093         return false;
40094     },
40095
40096     /**
40097      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40098      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40099      * @param {Number} index The zero-based index of the list item to select
40100      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40101      * selected item if it is not currently in view (defaults to true)
40102      */
40103     select : function(index, scrollIntoView){
40104         this.selectedIndex = index;
40105         this.view.select(index);
40106         if(scrollIntoView !== false){
40107             var el = this.view.getNode(index);
40108             if(el){
40109                 this.innerList.scrollChildIntoView(el, false);
40110             }
40111         }
40112     },
40113
40114     // private
40115     selectNext : function(){
40116         var ct = this.store.getCount();
40117         if(ct > 0){
40118             if(this.selectedIndex == -1){
40119                 this.select(0);
40120             }else if(this.selectedIndex < ct-1){
40121                 this.select(this.selectedIndex+1);
40122             }
40123         }
40124     },
40125
40126     // private
40127     selectPrev : function(){
40128         var ct = this.store.getCount();
40129         if(ct > 0){
40130             if(this.selectedIndex == -1){
40131                 this.select(0);
40132             }else if(this.selectedIndex != 0){
40133                 this.select(this.selectedIndex-1);
40134             }
40135         }
40136     },
40137
40138     // private
40139     onKeyUp : function(e){
40140         if(this.editable !== false && !e.isSpecialKey()){
40141             this.lastKey = e.getKey();
40142             this.dqTask.delay(this.queryDelay);
40143         }
40144     },
40145
40146     // private
40147     validateBlur : function(){
40148         return !this.list || !this.list.isVisible();   
40149     },
40150
40151     // private
40152     initQuery : function(){
40153         this.doQuery(this.getRawValue());
40154     },
40155
40156     // private
40157     doForce : function(){
40158         if(this.el.dom.value.length > 0){
40159             this.el.dom.value =
40160                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40161              
40162         }
40163     },
40164
40165     /**
40166      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40167      * query allowing the query action to be canceled if needed.
40168      * @param {String} query The SQL query to execute
40169      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40170      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40171      * saved in the current store (defaults to false)
40172      */
40173     doQuery : function(q, forceAll){
40174         if(q === undefined || q === null){
40175             q = '';
40176         }
40177         var qe = {
40178             query: q,
40179             forceAll: forceAll,
40180             combo: this,
40181             cancel:false
40182         };
40183         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40184             return false;
40185         }
40186         q = qe.query;
40187         forceAll = qe.forceAll;
40188         if(forceAll === true || (q.length >= this.minChars)){
40189             if(this.lastQuery != q || this.alwaysQuery){
40190                 this.lastQuery = q;
40191                 if(this.mode == 'local'){
40192                     this.selectedIndex = -1;
40193                     if(forceAll){
40194                         this.store.clearFilter();
40195                     }else{
40196                         this.store.filter(this.displayField, q);
40197                     }
40198                     this.onLoad();
40199                 }else{
40200                     this.store.baseParams[this.queryParam] = q;
40201                     this.store.load({
40202                         params: this.getParams(q)
40203                     });
40204                     this.expand();
40205                 }
40206             }else{
40207                 this.selectedIndex = -1;
40208                 this.onLoad();   
40209             }
40210         }
40211     },
40212
40213     // private
40214     getParams : function(q){
40215         var p = {};
40216         //p[this.queryParam] = q;
40217         if(this.pageSize){
40218             p.start = 0;
40219             p.limit = this.pageSize;
40220         }
40221         return p;
40222     },
40223
40224     /**
40225      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40226      */
40227     collapse : function(){
40228         if(!this.isExpanded()){
40229             return;
40230         }
40231         this.list.hide();
40232         Roo.get(document).un('mousedown', this.collapseIf, this);
40233         Roo.get(document).un('mousewheel', this.collapseIf, this);
40234         if (!this.editable) {
40235             Roo.get(document).un('keydown', this.listKeyPress, this);
40236         }
40237         this.fireEvent('collapse', this);
40238     },
40239
40240     // private
40241     collapseIf : function(e){
40242         if(!e.within(this.wrap) && !e.within(this.list)){
40243             this.collapse();
40244         }
40245     },
40246
40247     /**
40248      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40249      */
40250     expand : function(){
40251         if(this.isExpanded() || !this.hasFocus){
40252             return;
40253         }
40254         this.list.alignTo(this.el, this.listAlign);
40255         this.list.show();
40256         Roo.get(document).on('mousedown', this.collapseIf, this);
40257         Roo.get(document).on('mousewheel', this.collapseIf, this);
40258         if (!this.editable) {
40259             Roo.get(document).on('keydown', this.listKeyPress, this);
40260         }
40261         
40262         this.fireEvent('expand', this);
40263     },
40264
40265     // private
40266     // Implements the default empty TriggerField.onTriggerClick function
40267     onTriggerClick : function(){
40268         if(this.disabled){
40269             return;
40270         }
40271         if(this.isExpanded()){
40272             this.collapse();
40273             if (!this.blockFocus) {
40274                 this.el.focus();
40275             }
40276             
40277         }else {
40278             this.hasFocus = true;
40279             if(this.triggerAction == 'all') {
40280                 this.doQuery(this.allQuery, true);
40281             } else {
40282                 this.doQuery(this.getRawValue());
40283             }
40284             if (!this.blockFocus) {
40285                 this.el.focus();
40286             }
40287         }
40288     },
40289     listKeyPress : function(e)
40290     {
40291         //Roo.log('listkeypress');
40292         // scroll to first matching element based on key pres..
40293         if (e.isSpecialKey()) {
40294             return false;
40295         }
40296         var k = String.fromCharCode(e.getKey()).toUpperCase();
40297         //Roo.log(k);
40298         var match  = false;
40299         var csel = this.view.getSelectedNodes();
40300         var cselitem = false;
40301         if (csel.length) {
40302             var ix = this.view.indexOf(csel[0]);
40303             cselitem  = this.store.getAt(ix);
40304             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40305                 cselitem = false;
40306             }
40307             
40308         }
40309         
40310         this.store.each(function(v) { 
40311             if (cselitem) {
40312                 // start at existing selection.
40313                 if (cselitem.id == v.id) {
40314                     cselitem = false;
40315                 }
40316                 return;
40317             }
40318                 
40319             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40320                 match = this.store.indexOf(v);
40321                 return false;
40322             }
40323         }, this);
40324         
40325         if (match === false) {
40326             return true; // no more action?
40327         }
40328         // scroll to?
40329         this.view.select(match);
40330         var sn = Roo.get(this.view.getSelectedNodes()[0])
40331         sn.scrollIntoView(sn.dom.parentNode, false);
40332     }
40333
40334     /** 
40335     * @cfg {Boolean} grow 
40336     * @hide 
40337     */
40338     /** 
40339     * @cfg {Number} growMin 
40340     * @hide 
40341     */
40342     /** 
40343     * @cfg {Number} growMax 
40344     * @hide 
40345     */
40346     /**
40347      * @hide
40348      * @method autoSize
40349      */
40350 });/*
40351  * Copyright(c) 2010-2012, Roo J Solutions Limited
40352  *
40353  * Licence LGPL
40354  *
40355  */
40356
40357 /**
40358  * @class Roo.form.ComboBoxArray
40359  * @extends Roo.form.TextField
40360  * A facebook style adder... for lists of email / people / countries  etc...
40361  * pick multiple items from a combo box, and shows each one.
40362  *
40363  *  Fred [x]  Brian [x]  [Pick another |v]
40364  *
40365  *
40366  *  For this to work: it needs various extra information
40367  *    - normal combo problay has
40368  *      name, hiddenName
40369  *    + displayField, valueField
40370  *
40371  *    For our purpose...
40372  *
40373  *
40374  *   If we change from 'extends' to wrapping...
40375  *   
40376  *  
40377  *
40378  
40379  
40380  * @constructor
40381  * Create a new ComboBoxArray.
40382  * @param {Object} config Configuration options
40383  */
40384  
40385
40386 Roo.form.ComboBoxArray = function(config)
40387 {
40388     this.addEvents({
40389         /**
40390          * @event beforeremove
40391          * Fires before remove the value from the list
40392              * @param {Roo.form.ComboBoxArray} _self This combo box array
40393              * @param {Roo.form.ComboBoxArray.Item} item removed item
40394              */
40395         'beforeremove' : true,
40396         /**
40397          * @event remove
40398          * Fires when remove the value from the list
40399              * @param {Roo.form.ComboBoxArray} _self This combo box array
40400              * @param {Roo.form.ComboBoxArray.Item} item removed item
40401              */
40402         'remove' : true
40403         
40404         
40405     });
40406     
40407     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40408     
40409     this.items = new Roo.util.MixedCollection(false);
40410     
40411     // construct the child combo...
40412     
40413     
40414     
40415     
40416    
40417     
40418 }
40419
40420  
40421 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40422
40423     /**
40424      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40425      */
40426     
40427     lastData : false,
40428     
40429     // behavies liek a hiddne field
40430     inputType:      'hidden',
40431     /**
40432      * @cfg {Number} width The width of the box that displays the selected element
40433      */ 
40434     width:          300,
40435
40436     
40437     
40438     /**
40439      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40440      */
40441     name : false,
40442     /**
40443      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40444      */
40445     hiddenName : false,
40446     
40447     
40448     // private the array of items that are displayed..
40449     items  : false,
40450     // private - the hidden field el.
40451     hiddenEl : false,
40452     // private - the filed el..
40453     el : false,
40454     
40455     //validateValue : function() { return true; }, // all values are ok!
40456     //onAddClick: function() { },
40457     
40458     onRender : function(ct, position) 
40459     {
40460         
40461         // create the standard hidden element
40462         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40463         
40464         
40465         // give fake names to child combo;
40466         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40467         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40468         
40469         this.combo = Roo.factory(this.combo, Roo.form);
40470         this.combo.onRender(ct, position);
40471         if (typeof(this.combo.width) != 'undefined') {
40472             this.combo.onResize(this.combo.width,0);
40473         }
40474         
40475         this.combo.initEvents();
40476         
40477         // assigned so form know we need to do this..
40478         this.store          = this.combo.store;
40479         this.valueField     = this.combo.valueField;
40480         this.displayField   = this.combo.displayField ;
40481         
40482         
40483         this.combo.wrap.addClass('x-cbarray-grp');
40484         
40485         var cbwrap = this.combo.wrap.createChild(
40486             {tag: 'div', cls: 'x-cbarray-cb'},
40487             this.combo.el.dom
40488         );
40489         
40490              
40491         this.hiddenEl = this.combo.wrap.createChild({
40492             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40493         });
40494         this.el = this.combo.wrap.createChild({
40495             tag: 'input',  type:'hidden' , name: this.name, value : ''
40496         });
40497          //   this.el.dom.removeAttribute("name");
40498         
40499         
40500         this.outerWrap = this.combo.wrap;
40501         this.wrap = cbwrap;
40502         
40503         this.outerWrap.setWidth(this.width);
40504         this.outerWrap.dom.removeChild(this.el.dom);
40505         
40506         this.wrap.dom.appendChild(this.el.dom);
40507         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40508         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40509         
40510         this.combo.trigger.setStyle('position','relative');
40511         this.combo.trigger.setStyle('left', '0px');
40512         this.combo.trigger.setStyle('top', '2px');
40513         
40514         this.combo.el.setStyle('vertical-align', 'text-bottom');
40515         
40516         //this.trigger.setStyle('vertical-align', 'top');
40517         
40518         // this should use the code from combo really... on('add' ....)
40519         if (this.adder) {
40520             
40521         
40522             this.adder = this.outerWrap.createChild(
40523                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40524             var _t = this;
40525             this.adder.on('click', function(e) {
40526                 _t.fireEvent('adderclick', this, e);
40527             }, _t);
40528         }
40529         //var _t = this;
40530         //this.adder.on('click', this.onAddClick, _t);
40531         
40532         
40533         this.combo.on('select', function(cb, rec, ix) {
40534             this.addItem(rec.data);
40535             
40536             cb.setValue('');
40537             cb.el.dom.value = '';
40538             //cb.lastData = rec.data;
40539             // add to list
40540             
40541         }, this);
40542         
40543         
40544     },
40545     
40546     
40547     getName: function()
40548     {
40549         // returns hidden if it's set..
40550         if (!this.rendered) {return ''};
40551         return  this.hiddenName ? this.hiddenName : this.name;
40552         
40553     },
40554     
40555     
40556     onResize: function(w, h){
40557         
40558         return;
40559         // not sure if this is needed..
40560         //this.combo.onResize(w,h);
40561         
40562         if(typeof w != 'number'){
40563             // we do not handle it!?!?
40564             return;
40565         }
40566         var tw = this.combo.trigger.getWidth();
40567         tw += this.addicon ? this.addicon.getWidth() : 0;
40568         tw += this.editicon ? this.editicon.getWidth() : 0;
40569         var x = w - tw;
40570         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40571             
40572         this.combo.trigger.setStyle('left', '0px');
40573         
40574         if(this.list && this.listWidth === undefined){
40575             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40576             this.list.setWidth(lw);
40577             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40578         }
40579         
40580     
40581         
40582     },
40583     
40584     addItem: function(rec)
40585     {
40586         var valueField = this.combo.valueField;
40587         var displayField = this.combo.displayField;
40588         if (this.items.indexOfKey(rec[valueField]) > -1) {
40589             //console.log("GOT " + rec.data.id);
40590             return;
40591         }
40592         
40593         var x = new Roo.form.ComboBoxArray.Item({
40594             //id : rec[this.idField],
40595             data : rec,
40596             displayField : displayField ,
40597             tipField : displayField ,
40598             cb : this
40599         });
40600         // use the 
40601         this.items.add(rec[valueField],x);
40602         // add it before the element..
40603         this.updateHiddenEl();
40604         x.render(this.outerWrap, this.wrap.dom);
40605         // add the image handler..
40606     },
40607     
40608     updateHiddenEl : function()
40609     {
40610         this.validate();
40611         if (!this.hiddenEl) {
40612             return;
40613         }
40614         var ar = [];
40615         var idField = this.combo.valueField;
40616         
40617         this.items.each(function(f) {
40618             ar.push(f.data[idField]);
40619            
40620         });
40621         this.hiddenEl.dom.value = ar.join(',');
40622         this.validate();
40623     },
40624     
40625     reset : function()
40626     {
40627         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40628         this.items.each(function(f) {
40629            f.remove(); 
40630         });
40631         this.el.dom.value = '';
40632         if (this.hiddenEl) {
40633             this.hiddenEl.dom.value = '';
40634         }
40635         
40636     },
40637     getValue: function()
40638     {
40639         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40640     },
40641     setValue: function(v) // not a valid action - must use addItems..
40642     {
40643          
40644         this.reset();
40645         
40646         
40647         
40648         if (this.store.isLocal && (typeof(v) == 'string')) {
40649             // then we can use the store to find the values..
40650             // comma seperated at present.. this needs to allow JSON based encoding..
40651             this.hiddenEl.value  = v;
40652             var v_ar = [];
40653             Roo.each(v.split(','), function(k) {
40654                 Roo.log("CHECK " + this.valueField + ',' + k);
40655                 var li = this.store.query(this.valueField, k);
40656                 if (!li.length) {
40657                     return;
40658                 }
40659                 var add = {};
40660                 add[this.valueField] = k;
40661                 add[this.displayField] = li.item(0).data[this.displayField];
40662                 
40663                 this.addItem(add);
40664             }, this) 
40665              
40666         }
40667         if (typeof(v) == 'object' ) {
40668             // then let's assume it's an array of objects..
40669             Roo.each(v, function(l) {
40670                 this.addItem(l);
40671             }, this);
40672              
40673         }
40674         
40675         
40676     },
40677     setFromData: function(v)
40678     {
40679         // this recieves an object, if setValues is called.
40680         this.reset();
40681         this.el.dom.value = v[this.displayField];
40682         this.hiddenEl.dom.value = v[this.valueField];
40683         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40684             return;
40685         }
40686         var kv = v[this.valueField];
40687         var dv = v[this.displayField];
40688         kv = typeof(kv) != 'string' ? '' : kv;
40689         dv = typeof(dv) != 'string' ? '' : dv;
40690         
40691         
40692         var keys = kv.split(',');
40693         var display = dv.split(',');
40694         for (var i = 0 ; i < keys.length; i++) {
40695             
40696             add = {};
40697             add[this.valueField] = keys[i];
40698             add[this.displayField] = display[i];
40699             this.addItem(add);
40700         }
40701       
40702         
40703     },
40704     
40705     /**
40706      * Validates the combox array value
40707      * @return {Boolean} True if the value is valid, else false
40708      */
40709     validate : function(){
40710         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40711             this.clearInvalid();
40712             return true;
40713         }
40714         return false;
40715     },
40716     
40717     validateValue : function(value){
40718         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40719         
40720     },
40721     
40722     /*@
40723      * overide
40724      * 
40725      */
40726     isDirty : function() {
40727         if(this.disabled) {
40728             return false;
40729         }
40730         
40731         try {
40732             var d = Roo.decode(String(this.originalValue));
40733         } catch (e) {
40734             return String(this.getValue()) !== String(this.originalValue);
40735         }
40736         
40737         var originalValue = [];
40738         
40739         for (var i = 0; i < d.length; i++){
40740             originalValue.push(d[i][this.valueField]);
40741         }
40742         
40743         return String(this.getValue()) !== String(originalValue.join(','));
40744         
40745     }
40746     
40747 });
40748
40749
40750
40751 /**
40752  * @class Roo.form.ComboBoxArray.Item
40753  * @extends Roo.BoxComponent
40754  * A selected item in the list
40755  *  Fred [x]  Brian [x]  [Pick another |v]
40756  * 
40757  * @constructor
40758  * Create a new item.
40759  * @param {Object} config Configuration options
40760  */
40761  
40762 Roo.form.ComboBoxArray.Item = function(config) {
40763     config.id = Roo.id();
40764     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40765 }
40766
40767 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40768     data : {},
40769     cb: false,
40770     displayField : false,
40771     tipField : false,
40772     
40773     
40774     defaultAutoCreate : {
40775         tag: 'div',
40776         cls: 'x-cbarray-item',
40777         cn : [ 
40778             { tag: 'div' },
40779             {
40780                 tag: 'img',
40781                 width:16,
40782                 height : 16,
40783                 src : Roo.BLANK_IMAGE_URL ,
40784                 align: 'center'
40785             }
40786         ]
40787         
40788     },
40789     
40790  
40791     onRender : function(ct, position)
40792     {
40793         Roo.form.Field.superclass.onRender.call(this, ct, position);
40794         
40795         if(!this.el){
40796             var cfg = this.getAutoCreate();
40797             this.el = ct.createChild(cfg, position);
40798         }
40799         
40800         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40801         
40802         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40803             this.cb.renderer(this.data) :
40804             String.format('{0}',this.data[this.displayField]);
40805         
40806             
40807         this.el.child('div').dom.setAttribute('qtip',
40808                         String.format('{0}',this.data[this.tipField])
40809         );
40810         
40811         this.el.child('img').on('click', this.remove, this);
40812         
40813     },
40814    
40815     remove : function()
40816     {
40817         if(this.cb.disabled){
40818             return;
40819         }
40820         
40821         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40822             this.cb.items.remove(this);
40823             this.el.child('img').un('click', this.remove, this);
40824             this.el.remove();
40825             this.cb.updateHiddenEl();
40826
40827             this.cb.fireEvent('remove', this.cb, this);
40828         }
40829         
40830     }
40831 });/*
40832  * Based on:
40833  * Ext JS Library 1.1.1
40834  * Copyright(c) 2006-2007, Ext JS, LLC.
40835  *
40836  * Originally Released Under LGPL - original licence link has changed is not relivant.
40837  *
40838  * Fork - LGPL
40839  * <script type="text/javascript">
40840  */
40841 /**
40842  * @class Roo.form.Checkbox
40843  * @extends Roo.form.Field
40844  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40845  * @constructor
40846  * Creates a new Checkbox
40847  * @param {Object} config Configuration options
40848  */
40849 Roo.form.Checkbox = function(config){
40850     Roo.form.Checkbox.superclass.constructor.call(this, config);
40851     this.addEvents({
40852         /**
40853          * @event check
40854          * Fires when the checkbox is checked or unchecked.
40855              * @param {Roo.form.Checkbox} this This checkbox
40856              * @param {Boolean} checked The new checked value
40857              */
40858         check : true
40859     });
40860 };
40861
40862 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40863     /**
40864      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40865      */
40866     focusClass : undefined,
40867     /**
40868      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40869      */
40870     fieldClass: "x-form-field",
40871     /**
40872      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40873      */
40874     checked: false,
40875     /**
40876      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40877      * {tag: "input", type: "checkbox", autocomplete: "off"})
40878      */
40879     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40880     /**
40881      * @cfg {String} boxLabel The text that appears beside the checkbox
40882      */
40883     boxLabel : "",
40884     /**
40885      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40886      */  
40887     inputValue : '1',
40888     /**
40889      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40890      */
40891      valueOff: '0', // value when not checked..
40892
40893     actionMode : 'viewEl', 
40894     //
40895     // private
40896     itemCls : 'x-menu-check-item x-form-item',
40897     groupClass : 'x-menu-group-item',
40898     inputType : 'hidden',
40899     
40900     
40901     inSetChecked: false, // check that we are not calling self...
40902     
40903     inputElement: false, // real input element?
40904     basedOn: false, // ????
40905     
40906     isFormField: true, // not sure where this is needed!!!!
40907
40908     onResize : function(){
40909         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40910         if(!this.boxLabel){
40911             this.el.alignTo(this.wrap, 'c-c');
40912         }
40913     },
40914
40915     initEvents : function(){
40916         Roo.form.Checkbox.superclass.initEvents.call(this);
40917         this.el.on("click", this.onClick,  this);
40918         this.el.on("change", this.onClick,  this);
40919     },
40920
40921
40922     getResizeEl : function(){
40923         return this.wrap;
40924     },
40925
40926     getPositionEl : function(){
40927         return this.wrap;
40928     },
40929
40930     // private
40931     onRender : function(ct, position){
40932         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40933         /*
40934         if(this.inputValue !== undefined){
40935             this.el.dom.value = this.inputValue;
40936         }
40937         */
40938         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40939         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40940         var viewEl = this.wrap.createChild({ 
40941             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40942         this.viewEl = viewEl;   
40943         this.wrap.on('click', this.onClick,  this); 
40944         
40945         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40946         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40947         
40948         
40949         
40950         if(this.boxLabel){
40951             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40952         //    viewEl.on('click', this.onClick,  this); 
40953         }
40954         //if(this.checked){
40955             this.setChecked(this.checked);
40956         //}else{
40957             //this.checked = this.el.dom;
40958         //}
40959
40960     },
40961
40962     // private
40963     initValue : Roo.emptyFn,
40964
40965     /**
40966      * Returns the checked state of the checkbox.
40967      * @return {Boolean} True if checked, else false
40968      */
40969     getValue : function(){
40970         if(this.el){
40971             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40972         }
40973         return this.valueOff;
40974         
40975     },
40976
40977         // private
40978     onClick : function(){ 
40979         if (this.disabled) {
40980             return;
40981         }
40982         this.setChecked(!this.checked);
40983
40984         //if(this.el.dom.checked != this.checked){
40985         //    this.setValue(this.el.dom.checked);
40986        // }
40987     },
40988
40989     /**
40990      * Sets the checked state of the checkbox.
40991      * On is always based on a string comparison between inputValue and the param.
40992      * @param {Boolean/String} value - the value to set 
40993      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40994      */
40995     setValue : function(v,suppressEvent){
40996         
40997         
40998         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40999         //if(this.el && this.el.dom){
41000         //    this.el.dom.checked = this.checked;
41001         //    this.el.dom.defaultChecked = this.checked;
41002         //}
41003         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
41004         //this.fireEvent("check", this, this.checked);
41005     },
41006     // private..
41007     setChecked : function(state,suppressEvent)
41008     {
41009         if (this.inSetChecked) {
41010             this.checked = state;
41011             return;
41012         }
41013         
41014     
41015         if(this.wrap){
41016             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41017         }
41018         this.checked = state;
41019         if(suppressEvent !== true){
41020             this.fireEvent('check', this, state);
41021         }
41022         this.inSetChecked = true;
41023         this.el.dom.value = state ? this.inputValue : this.valueOff;
41024         this.inSetChecked = false;
41025         
41026     },
41027     // handle setting of hidden value by some other method!!?!?
41028     setFromHidden: function()
41029     {
41030         if(!this.el){
41031             return;
41032         }
41033         //console.log("SET FROM HIDDEN");
41034         //alert('setFrom hidden');
41035         this.setValue(this.el.dom.value);
41036     },
41037     
41038     onDestroy : function()
41039     {
41040         if(this.viewEl){
41041             Roo.get(this.viewEl).remove();
41042         }
41043          
41044         Roo.form.Checkbox.superclass.onDestroy.call(this);
41045     }
41046
41047 });/*
41048  * Based on:
41049  * Ext JS Library 1.1.1
41050  * Copyright(c) 2006-2007, Ext JS, LLC.
41051  *
41052  * Originally Released Under LGPL - original licence link has changed is not relivant.
41053  *
41054  * Fork - LGPL
41055  * <script type="text/javascript">
41056  */
41057  
41058 /**
41059  * @class Roo.form.Radio
41060  * @extends Roo.form.Checkbox
41061  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41062  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41063  * @constructor
41064  * Creates a new Radio
41065  * @param {Object} config Configuration options
41066  */
41067 Roo.form.Radio = function(){
41068     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41069 };
41070 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41071     inputType: 'radio',
41072
41073     /**
41074      * If this radio is part of a group, it will return the selected value
41075      * @return {String}
41076      */
41077     getGroupValue : function(){
41078         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41079     },
41080     
41081     
41082     onRender : function(ct, position){
41083         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41084         
41085         if(this.inputValue !== undefined){
41086             this.el.dom.value = this.inputValue;
41087         }
41088          
41089         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41090         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41091         //var viewEl = this.wrap.createChild({ 
41092         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41093         //this.viewEl = viewEl;   
41094         //this.wrap.on('click', this.onClick,  this); 
41095         
41096         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41097         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41098         
41099         
41100         
41101         if(this.boxLabel){
41102             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41103         //    viewEl.on('click', this.onClick,  this); 
41104         }
41105          if(this.checked){
41106             this.el.dom.checked =   'checked' ;
41107         }
41108          
41109     } 
41110     
41111     
41112 });//<script type="text/javascript">
41113
41114 /*
41115  * Based  Ext JS Library 1.1.1
41116  * Copyright(c) 2006-2007, Ext JS, LLC.
41117  * LGPL
41118  *
41119  */
41120  
41121 /**
41122  * @class Roo.HtmlEditorCore
41123  * @extends Roo.Component
41124  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41125  *
41126  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41127  */
41128
41129 Roo.HtmlEditorCore = function(config){
41130     
41131     
41132     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41133     
41134     
41135     this.addEvents({
41136         /**
41137          * @event initialize
41138          * Fires when the editor is fully initialized (including the iframe)
41139          * @param {Roo.HtmlEditorCore} this
41140          */
41141         initialize: true,
41142         /**
41143          * @event activate
41144          * Fires when the editor is first receives the focus. Any insertion must wait
41145          * until after this event.
41146          * @param {Roo.HtmlEditorCore} this
41147          */
41148         activate: true,
41149          /**
41150          * @event beforesync
41151          * Fires before the textarea is updated with content from the editor iframe. Return false
41152          * to cancel the sync.
41153          * @param {Roo.HtmlEditorCore} this
41154          * @param {String} html
41155          */
41156         beforesync: true,
41157          /**
41158          * @event beforepush
41159          * Fires before the iframe editor is updated with content from the textarea. Return false
41160          * to cancel the push.
41161          * @param {Roo.HtmlEditorCore} this
41162          * @param {String} html
41163          */
41164         beforepush: true,
41165          /**
41166          * @event sync
41167          * Fires when the textarea is updated with content from the editor iframe.
41168          * @param {Roo.HtmlEditorCore} this
41169          * @param {String} html
41170          */
41171         sync: true,
41172          /**
41173          * @event push
41174          * Fires when the iframe editor is updated with content from the textarea.
41175          * @param {Roo.HtmlEditorCore} this
41176          * @param {String} html
41177          */
41178         push: true,
41179         
41180         /**
41181          * @event editorevent
41182          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41183          * @param {Roo.HtmlEditorCore} this
41184          */
41185         editorevent: true
41186         
41187     });
41188     
41189     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41190     
41191     // defaults : white / black...
41192     this.applyBlacklists();
41193     
41194     
41195     
41196 };
41197
41198
41199 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41200
41201
41202      /**
41203      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41204      */
41205     
41206     owner : false,
41207     
41208      /**
41209      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41210      *                        Roo.resizable.
41211      */
41212     resizable : false,
41213      /**
41214      * @cfg {Number} height (in pixels)
41215      */   
41216     height: 300,
41217    /**
41218      * @cfg {Number} width (in pixels)
41219      */   
41220     width: 500,
41221     
41222     /**
41223      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41224      * 
41225      */
41226     stylesheets: false,
41227     
41228     // id of frame..
41229     frameId: false,
41230     
41231     // private properties
41232     validationEvent : false,
41233     deferHeight: true,
41234     initialized : false,
41235     activated : false,
41236     sourceEditMode : false,
41237     onFocus : Roo.emptyFn,
41238     iframePad:3,
41239     hideMode:'offsets',
41240     
41241     clearUp: true,
41242     
41243     // blacklist + whitelisted elements..
41244     black: false,
41245     white: false,
41246      
41247     
41248
41249     /**
41250      * Protected method that will not generally be called directly. It
41251      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41252      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41253      */
41254     getDocMarkup : function(){
41255         // body styles..
41256         var st = '';
41257         
41258         // inherit styels from page...?? 
41259         if (this.stylesheets === false) {
41260             
41261             Roo.get(document.head).select('style').each(function(node) {
41262                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41263             });
41264             
41265             Roo.get(document.head).select('link').each(function(node) { 
41266                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41267             });
41268             
41269         } else if (!this.stylesheets.length) {
41270                 // simple..
41271                 st = '<style type="text/css">' +
41272                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41273                    '</style>';
41274         } else { 
41275             
41276         }
41277         
41278         st +=  '<style type="text/css">' +
41279             'IMG { cursor: pointer } ' +
41280         '</style>';
41281
41282         
41283         return '<html><head>' + st  +
41284             //<style type="text/css">' +
41285             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41286             //'</style>' +
41287             ' </head><body class="roo-htmleditor-body"></body></html>';
41288     },
41289
41290     // private
41291     onRender : function(ct, position)
41292     {
41293         var _t = this;
41294         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41295         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41296         
41297         
41298         this.el.dom.style.border = '0 none';
41299         this.el.dom.setAttribute('tabIndex', -1);
41300         this.el.addClass('x-hidden hide');
41301         
41302         
41303         
41304         if(Roo.isIE){ // fix IE 1px bogus margin
41305             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41306         }
41307        
41308         
41309         this.frameId = Roo.id();
41310         
41311          
41312         
41313         var iframe = this.owner.wrap.createChild({
41314             tag: 'iframe',
41315             cls: 'form-control', // bootstrap..
41316             id: this.frameId,
41317             name: this.frameId,
41318             frameBorder : 'no',
41319             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41320         }, this.el
41321         );
41322         
41323         
41324         this.iframe = iframe.dom;
41325
41326          this.assignDocWin();
41327         
41328         this.doc.designMode = 'on';
41329        
41330         this.doc.open();
41331         this.doc.write(this.getDocMarkup());
41332         this.doc.close();
41333
41334         
41335         var task = { // must defer to wait for browser to be ready
41336             run : function(){
41337                 //console.log("run task?" + this.doc.readyState);
41338                 this.assignDocWin();
41339                 if(this.doc.body || this.doc.readyState == 'complete'){
41340                     try {
41341                         this.doc.designMode="on";
41342                     } catch (e) {
41343                         return;
41344                     }
41345                     Roo.TaskMgr.stop(task);
41346                     this.initEditor.defer(10, this);
41347                 }
41348             },
41349             interval : 10,
41350             duration: 10000,
41351             scope: this
41352         };
41353         Roo.TaskMgr.start(task);
41354
41355     },
41356
41357     // private
41358     onResize : function(w, h)
41359     {
41360          Roo.log('resize: ' +w + ',' + h );
41361         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41362         if(!this.iframe){
41363             return;
41364         }
41365         if(typeof w == 'number'){
41366             
41367             this.iframe.style.width = w + 'px';
41368         }
41369         if(typeof h == 'number'){
41370             
41371             this.iframe.style.height = h + 'px';
41372             if(this.doc){
41373                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41374             }
41375         }
41376         
41377     },
41378
41379     /**
41380      * Toggles the editor between standard and source edit mode.
41381      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41382      */
41383     toggleSourceEdit : function(sourceEditMode){
41384         
41385         this.sourceEditMode = sourceEditMode === true;
41386         
41387         if(this.sourceEditMode){
41388  
41389             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41390             
41391         }else{
41392             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41393             //this.iframe.className = '';
41394             this.deferFocus();
41395         }
41396         //this.setSize(this.owner.wrap.getSize());
41397         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41398     },
41399
41400     
41401   
41402
41403     /**
41404      * Protected method that will not generally be called directly. If you need/want
41405      * custom HTML cleanup, this is the method you should override.
41406      * @param {String} html The HTML to be cleaned
41407      * return {String} The cleaned HTML
41408      */
41409     cleanHtml : function(html){
41410         html = String(html);
41411         if(html.length > 5){
41412             if(Roo.isSafari){ // strip safari nonsense
41413                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41414             }
41415         }
41416         if(html == '&nbsp;'){
41417             html = '';
41418         }
41419         return html;
41420     },
41421
41422     /**
41423      * HTML Editor -> Textarea
41424      * Protected method that will not generally be called directly. Syncs the contents
41425      * of the editor iframe with the textarea.
41426      */
41427     syncValue : function(){
41428         if(this.initialized){
41429             var bd = (this.doc.body || this.doc.documentElement);
41430             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41431             var html = bd.innerHTML;
41432             if(Roo.isSafari){
41433                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41434                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41435                 if(m && m[1]){
41436                     html = '<div style="'+m[0]+'">' + html + '</div>';
41437                 }
41438             }
41439             html = this.cleanHtml(html);
41440             // fix up the special chars.. normaly like back quotes in word...
41441             // however we do not want to do this with chinese..
41442             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41443                 var cc = b.charCodeAt();
41444                 if (
41445                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41446                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41447                     (cc >= 0xf900 && cc < 0xfb00 )
41448                 ) {
41449                         return b;
41450                 }
41451                 return "&#"+cc+";" 
41452             });
41453             if(this.owner.fireEvent('beforesync', this, html) !== false){
41454                 this.el.dom.value = html;
41455                 this.owner.fireEvent('sync', this, html);
41456             }
41457         }
41458     },
41459
41460     /**
41461      * Protected method that will not generally be called directly. Pushes the value of the textarea
41462      * into the iframe editor.
41463      */
41464     pushValue : function(){
41465         if(this.initialized){
41466             var v = this.el.dom.value.trim();
41467             
41468 //            if(v.length < 1){
41469 //                v = '&#160;';
41470 //            }
41471             
41472             if(this.owner.fireEvent('beforepush', this, v) !== false){
41473                 var d = (this.doc.body || this.doc.documentElement);
41474                 d.innerHTML = v;
41475                 this.cleanUpPaste();
41476                 this.el.dom.value = d.innerHTML;
41477                 this.owner.fireEvent('push', this, v);
41478             }
41479         }
41480     },
41481
41482     // private
41483     deferFocus : function(){
41484         this.focus.defer(10, this);
41485     },
41486
41487     // doc'ed in Field
41488     focus : function(){
41489         if(this.win && !this.sourceEditMode){
41490             this.win.focus();
41491         }else{
41492             this.el.focus();
41493         }
41494     },
41495     
41496     assignDocWin: function()
41497     {
41498         var iframe = this.iframe;
41499         
41500          if(Roo.isIE){
41501             this.doc = iframe.contentWindow.document;
41502             this.win = iframe.contentWindow;
41503         } else {
41504 //            if (!Roo.get(this.frameId)) {
41505 //                return;
41506 //            }
41507 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41508 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41509             
41510             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41511                 return;
41512             }
41513             
41514             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41515             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41516         }
41517     },
41518     
41519     // private
41520     initEditor : function(){
41521         //console.log("INIT EDITOR");
41522         this.assignDocWin();
41523         
41524         
41525         
41526         this.doc.designMode="on";
41527         this.doc.open();
41528         this.doc.write(this.getDocMarkup());
41529         this.doc.close();
41530         
41531         var dbody = (this.doc.body || this.doc.documentElement);
41532         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41533         // this copies styles from the containing element into thsi one..
41534         // not sure why we need all of this..
41535         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41536         
41537         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41538         //ss['background-attachment'] = 'fixed'; // w3c
41539         dbody.bgProperties = 'fixed'; // ie
41540         //Roo.DomHelper.applyStyles(dbody, ss);
41541         Roo.EventManager.on(this.doc, {
41542             //'mousedown': this.onEditorEvent,
41543             'mouseup': this.onEditorEvent,
41544             'dblclick': this.onEditorEvent,
41545             'click': this.onEditorEvent,
41546             'keyup': this.onEditorEvent,
41547             buffer:100,
41548             scope: this
41549         });
41550         if(Roo.isGecko){
41551             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41552         }
41553         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41554             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41555         }
41556         this.initialized = true;
41557
41558         this.owner.fireEvent('initialize', this);
41559         this.pushValue();
41560     },
41561
41562     // private
41563     onDestroy : function(){
41564         
41565         
41566         
41567         if(this.rendered){
41568             
41569             //for (var i =0; i < this.toolbars.length;i++) {
41570             //    // fixme - ask toolbars for heights?
41571             //    this.toolbars[i].onDestroy();
41572            // }
41573             
41574             //this.wrap.dom.innerHTML = '';
41575             //this.wrap.remove();
41576         }
41577     },
41578
41579     // private
41580     onFirstFocus : function(){
41581         
41582         this.assignDocWin();
41583         
41584         
41585         this.activated = true;
41586          
41587     
41588         if(Roo.isGecko){ // prevent silly gecko errors
41589             this.win.focus();
41590             var s = this.win.getSelection();
41591             if(!s.focusNode || s.focusNode.nodeType != 3){
41592                 var r = s.getRangeAt(0);
41593                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41594                 r.collapse(true);
41595                 this.deferFocus();
41596             }
41597             try{
41598                 this.execCmd('useCSS', true);
41599                 this.execCmd('styleWithCSS', false);
41600             }catch(e){}
41601         }
41602         this.owner.fireEvent('activate', this);
41603     },
41604
41605     // private
41606     adjustFont: function(btn){
41607         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41608         //if(Roo.isSafari){ // safari
41609         //    adjust *= 2;
41610        // }
41611         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41612         if(Roo.isSafari){ // safari
41613             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41614             v =  (v < 10) ? 10 : v;
41615             v =  (v > 48) ? 48 : v;
41616             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41617             
41618         }
41619         
41620         
41621         v = Math.max(1, v+adjust);
41622         
41623         this.execCmd('FontSize', v  );
41624     },
41625
41626     onEditorEvent : function(e)
41627     {
41628         this.owner.fireEvent('editorevent', this, e);
41629       //  this.updateToolbar();
41630         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41631     },
41632
41633     insertTag : function(tg)
41634     {
41635         // could be a bit smarter... -> wrap the current selected tRoo..
41636         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41637             
41638             range = this.createRange(this.getSelection());
41639             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41640             wrappingNode.appendChild(range.extractContents());
41641             range.insertNode(wrappingNode);
41642
41643             return;
41644             
41645             
41646             
41647         }
41648         this.execCmd("formatblock",   tg);
41649         
41650     },
41651     
41652     insertText : function(txt)
41653     {
41654         
41655         
41656         var range = this.createRange();
41657         range.deleteContents();
41658                //alert(Sender.getAttribute('label'));
41659                
41660         range.insertNode(this.doc.createTextNode(txt));
41661     } ,
41662     
41663      
41664
41665     /**
41666      * Executes a Midas editor command on the editor document and performs necessary focus and
41667      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41668      * @param {String} cmd The Midas command
41669      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41670      */
41671     relayCmd : function(cmd, value){
41672         this.win.focus();
41673         this.execCmd(cmd, value);
41674         this.owner.fireEvent('editorevent', this);
41675         //this.updateToolbar();
41676         this.owner.deferFocus();
41677     },
41678
41679     /**
41680      * Executes a Midas editor command directly on the editor document.
41681      * For visual commands, you should use {@link #relayCmd} instead.
41682      * <b>This should only be called after the editor is initialized.</b>
41683      * @param {String} cmd The Midas command
41684      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41685      */
41686     execCmd : function(cmd, value){
41687         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41688         this.syncValue();
41689     },
41690  
41691  
41692    
41693     /**
41694      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41695      * to insert tRoo.
41696      * @param {String} text | dom node.. 
41697      */
41698     insertAtCursor : function(text)
41699     {
41700         
41701         
41702         
41703         if(!this.activated){
41704             return;
41705         }
41706         /*
41707         if(Roo.isIE){
41708             this.win.focus();
41709             var r = this.doc.selection.createRange();
41710             if(r){
41711                 r.collapse(true);
41712                 r.pasteHTML(text);
41713                 this.syncValue();
41714                 this.deferFocus();
41715             
41716             }
41717             return;
41718         }
41719         */
41720         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41721             this.win.focus();
41722             
41723             
41724             // from jquery ui (MIT licenced)
41725             var range, node;
41726             var win = this.win;
41727             
41728             if (win.getSelection && win.getSelection().getRangeAt) {
41729                 range = win.getSelection().getRangeAt(0);
41730                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41731                 range.insertNode(node);
41732             } else if (win.document.selection && win.document.selection.createRange) {
41733                 // no firefox support
41734                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41735                 win.document.selection.createRange().pasteHTML(txt);
41736             } else {
41737                 // no firefox support
41738                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41739                 this.execCmd('InsertHTML', txt);
41740             } 
41741             
41742             this.syncValue();
41743             
41744             this.deferFocus();
41745         }
41746     },
41747  // private
41748     mozKeyPress : function(e){
41749         if(e.ctrlKey){
41750             var c = e.getCharCode(), cmd;
41751           
41752             if(c > 0){
41753                 c = String.fromCharCode(c).toLowerCase();
41754                 switch(c){
41755                     case 'b':
41756                         cmd = 'bold';
41757                         break;
41758                     case 'i':
41759                         cmd = 'italic';
41760                         break;
41761                     
41762                     case 'u':
41763                         cmd = 'underline';
41764                         break;
41765                     
41766                     case 'v':
41767                         this.cleanUpPaste.defer(100, this);
41768                         return;
41769                         
41770                 }
41771                 if(cmd){
41772                     this.win.focus();
41773                     this.execCmd(cmd);
41774                     this.deferFocus();
41775                     e.preventDefault();
41776                 }
41777                 
41778             }
41779         }
41780     },
41781
41782     // private
41783     fixKeys : function(){ // load time branching for fastest keydown performance
41784         if(Roo.isIE){
41785             return function(e){
41786                 var k = e.getKey(), r;
41787                 if(k == e.TAB){
41788                     e.stopEvent();
41789                     r = this.doc.selection.createRange();
41790                     if(r){
41791                         r.collapse(true);
41792                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41793                         this.deferFocus();
41794                     }
41795                     return;
41796                 }
41797                 
41798                 if(k == e.ENTER){
41799                     r = this.doc.selection.createRange();
41800                     if(r){
41801                         var target = r.parentElement();
41802                         if(!target || target.tagName.toLowerCase() != 'li'){
41803                             e.stopEvent();
41804                             r.pasteHTML('<br />');
41805                             r.collapse(false);
41806                             r.select();
41807                         }
41808                     }
41809                 }
41810                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41811                     this.cleanUpPaste.defer(100, this);
41812                     return;
41813                 }
41814                 
41815                 
41816             };
41817         }else if(Roo.isOpera){
41818             return function(e){
41819                 var k = e.getKey();
41820                 if(k == e.TAB){
41821                     e.stopEvent();
41822                     this.win.focus();
41823                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41824                     this.deferFocus();
41825                 }
41826                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41827                     this.cleanUpPaste.defer(100, this);
41828                     return;
41829                 }
41830                 
41831             };
41832         }else if(Roo.isSafari){
41833             return function(e){
41834                 var k = e.getKey();
41835                 
41836                 if(k == e.TAB){
41837                     e.stopEvent();
41838                     this.execCmd('InsertText','\t');
41839                     this.deferFocus();
41840                     return;
41841                 }
41842                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41843                     this.cleanUpPaste.defer(100, this);
41844                     return;
41845                 }
41846                 
41847              };
41848         }
41849     }(),
41850     
41851     getAllAncestors: function()
41852     {
41853         var p = this.getSelectedNode();
41854         var a = [];
41855         if (!p) {
41856             a.push(p); // push blank onto stack..
41857             p = this.getParentElement();
41858         }
41859         
41860         
41861         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41862             a.push(p);
41863             p = p.parentNode;
41864         }
41865         a.push(this.doc.body);
41866         return a;
41867     },
41868     lastSel : false,
41869     lastSelNode : false,
41870     
41871     
41872     getSelection : function() 
41873     {
41874         this.assignDocWin();
41875         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41876     },
41877     
41878     getSelectedNode: function() 
41879     {
41880         // this may only work on Gecko!!!
41881         
41882         // should we cache this!!!!
41883         
41884         
41885         
41886          
41887         var range = this.createRange(this.getSelection()).cloneRange();
41888         
41889         if (Roo.isIE) {
41890             var parent = range.parentElement();
41891             while (true) {
41892                 var testRange = range.duplicate();
41893                 testRange.moveToElementText(parent);
41894                 if (testRange.inRange(range)) {
41895                     break;
41896                 }
41897                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41898                     break;
41899                 }
41900                 parent = parent.parentElement;
41901             }
41902             return parent;
41903         }
41904         
41905         // is ancestor a text element.
41906         var ac =  range.commonAncestorContainer;
41907         if (ac.nodeType == 3) {
41908             ac = ac.parentNode;
41909         }
41910         
41911         var ar = ac.childNodes;
41912          
41913         var nodes = [];
41914         var other_nodes = [];
41915         var has_other_nodes = false;
41916         for (var i=0;i<ar.length;i++) {
41917             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41918                 continue;
41919             }
41920             // fullly contained node.
41921             
41922             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41923                 nodes.push(ar[i]);
41924                 continue;
41925             }
41926             
41927             // probably selected..
41928             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41929                 other_nodes.push(ar[i]);
41930                 continue;
41931             }
41932             // outer..
41933             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41934                 continue;
41935             }
41936             
41937             
41938             has_other_nodes = true;
41939         }
41940         if (!nodes.length && other_nodes.length) {
41941             nodes= other_nodes;
41942         }
41943         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41944             return false;
41945         }
41946         
41947         return nodes[0];
41948     },
41949     createRange: function(sel)
41950     {
41951         // this has strange effects when using with 
41952         // top toolbar - not sure if it's a great idea.
41953         //this.editor.contentWindow.focus();
41954         if (typeof sel != "undefined") {
41955             try {
41956                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41957             } catch(e) {
41958                 return this.doc.createRange();
41959             }
41960         } else {
41961             return this.doc.createRange();
41962         }
41963     },
41964     getParentElement: function()
41965     {
41966         
41967         this.assignDocWin();
41968         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41969         
41970         var range = this.createRange(sel);
41971          
41972         try {
41973             var p = range.commonAncestorContainer;
41974             while (p.nodeType == 3) { // text node
41975                 p = p.parentNode;
41976             }
41977             return p;
41978         } catch (e) {
41979             return null;
41980         }
41981     
41982     },
41983     /***
41984      *
41985      * Range intersection.. the hard stuff...
41986      *  '-1' = before
41987      *  '0' = hits..
41988      *  '1' = after.
41989      *         [ -- selected range --- ]
41990      *   [fail]                        [fail]
41991      *
41992      *    basically..
41993      *      if end is before start or  hits it. fail.
41994      *      if start is after end or hits it fail.
41995      *
41996      *   if either hits (but other is outside. - then it's not 
41997      *   
41998      *    
41999      **/
42000     
42001     
42002     // @see http://www.thismuchiknow.co.uk/?p=64.
42003     rangeIntersectsNode : function(range, node)
42004     {
42005         var nodeRange = node.ownerDocument.createRange();
42006         try {
42007             nodeRange.selectNode(node);
42008         } catch (e) {
42009             nodeRange.selectNodeContents(node);
42010         }
42011     
42012         var rangeStartRange = range.cloneRange();
42013         rangeStartRange.collapse(true);
42014     
42015         var rangeEndRange = range.cloneRange();
42016         rangeEndRange.collapse(false);
42017     
42018         var nodeStartRange = nodeRange.cloneRange();
42019         nodeStartRange.collapse(true);
42020     
42021         var nodeEndRange = nodeRange.cloneRange();
42022         nodeEndRange.collapse(false);
42023     
42024         return rangeStartRange.compareBoundaryPoints(
42025                  Range.START_TO_START, nodeEndRange) == -1 &&
42026                rangeEndRange.compareBoundaryPoints(
42027                  Range.START_TO_START, nodeStartRange) == 1;
42028         
42029          
42030     },
42031     rangeCompareNode : function(range, node)
42032     {
42033         var nodeRange = node.ownerDocument.createRange();
42034         try {
42035             nodeRange.selectNode(node);
42036         } catch (e) {
42037             nodeRange.selectNodeContents(node);
42038         }
42039         
42040         
42041         range.collapse(true);
42042     
42043         nodeRange.collapse(true);
42044      
42045         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42046         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42047          
42048         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42049         
42050         var nodeIsBefore   =  ss == 1;
42051         var nodeIsAfter    = ee == -1;
42052         
42053         if (nodeIsBefore && nodeIsAfter)
42054             return 0; // outer
42055         if (!nodeIsBefore && nodeIsAfter)
42056             return 1; //right trailed.
42057         
42058         if (nodeIsBefore && !nodeIsAfter)
42059             return 2;  // left trailed.
42060         // fully contined.
42061         return 3;
42062     },
42063
42064     // private? - in a new class?
42065     cleanUpPaste :  function()
42066     {
42067         // cleans up the whole document..
42068         Roo.log('cleanuppaste');
42069         
42070         this.cleanUpChildren(this.doc.body);
42071         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42072         if (clean != this.doc.body.innerHTML) {
42073             this.doc.body.innerHTML = clean;
42074         }
42075         
42076     },
42077     
42078     cleanWordChars : function(input) {// change the chars to hex code
42079         var he = Roo.HtmlEditorCore;
42080         
42081         var output = input;
42082         Roo.each(he.swapCodes, function(sw) { 
42083             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42084             
42085             output = output.replace(swapper, sw[1]);
42086         });
42087         
42088         return output;
42089     },
42090     
42091     
42092     cleanUpChildren : function (n)
42093     {
42094         if (!n.childNodes.length) {
42095             return;
42096         }
42097         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42098            this.cleanUpChild(n.childNodes[i]);
42099         }
42100     },
42101     
42102     
42103         
42104     
42105     cleanUpChild : function (node)
42106     {
42107         var ed = this;
42108         //console.log(node);
42109         if (node.nodeName == "#text") {
42110             // clean up silly Windows -- stuff?
42111             return; 
42112         }
42113         if (node.nodeName == "#comment") {
42114             node.parentNode.removeChild(node);
42115             // clean up silly Windows -- stuff?
42116             return; 
42117         }
42118         var lcname = node.tagName.toLowerCase();
42119         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42120         // whitelist of tags..
42121         
42122         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42123             // remove node.
42124             node.parentNode.removeChild(node);
42125             return;
42126             
42127         }
42128         
42129         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42130         
42131         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42132         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42133         
42134         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42135         //    remove_keep_children = true;
42136         //}
42137         
42138         if (remove_keep_children) {
42139             this.cleanUpChildren(node);
42140             // inserts everything just before this node...
42141             while (node.childNodes.length) {
42142                 var cn = node.childNodes[0];
42143                 node.removeChild(cn);
42144                 node.parentNode.insertBefore(cn, node);
42145             }
42146             node.parentNode.removeChild(node);
42147             return;
42148         }
42149         
42150         if (!node.attributes || !node.attributes.length) {
42151             this.cleanUpChildren(node);
42152             return;
42153         }
42154         
42155         function cleanAttr(n,v)
42156         {
42157             
42158             if (v.match(/^\./) || v.match(/^\//)) {
42159                 return;
42160             }
42161             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42162                 return;
42163             }
42164             if (v.match(/^#/)) {
42165                 return;
42166             }
42167 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42168             node.removeAttribute(n);
42169             
42170         }
42171         
42172         var cwhite = this.cwhite;
42173         var cblack = this.cblack;
42174             
42175         function cleanStyle(n,v)
42176         {
42177             if (v.match(/expression/)) { //XSS?? should we even bother..
42178                 node.removeAttribute(n);
42179                 return;
42180             }
42181             
42182             var parts = v.split(/;/);
42183             var clean = [];
42184             
42185             Roo.each(parts, function(p) {
42186                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42187                 if (!p.length) {
42188                     return true;
42189                 }
42190                 var l = p.split(':').shift().replace(/\s+/g,'');
42191                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42192                 
42193                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42194 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42195                     //node.removeAttribute(n);
42196                     return true;
42197                 }
42198                 //Roo.log()
42199                 // only allow 'c whitelisted system attributes'
42200                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42201 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42202                     //node.removeAttribute(n);
42203                     return true;
42204                 }
42205                 
42206                 
42207                  
42208                 
42209                 clean.push(p);
42210                 return true;
42211             });
42212             if (clean.length) { 
42213                 node.setAttribute(n, clean.join(';'));
42214             } else {
42215                 node.removeAttribute(n);
42216             }
42217             
42218         }
42219         
42220         
42221         for (var i = node.attributes.length-1; i > -1 ; i--) {
42222             var a = node.attributes[i];
42223             //console.log(a);
42224             
42225             if (a.name.toLowerCase().substr(0,2)=='on')  {
42226                 node.removeAttribute(a.name);
42227                 continue;
42228             }
42229             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42230                 node.removeAttribute(a.name);
42231                 continue;
42232             }
42233             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42234                 cleanAttr(a.name,a.value); // fixme..
42235                 continue;
42236             }
42237             if (a.name == 'style') {
42238                 cleanStyle(a.name,a.value);
42239                 continue;
42240             }
42241             /// clean up MS crap..
42242             // tecnically this should be a list of valid class'es..
42243             
42244             
42245             if (a.name == 'class') {
42246                 if (a.value.match(/^Mso/)) {
42247                     node.className = '';
42248                 }
42249                 
42250                 if (a.value.match(/body/)) {
42251                     node.className = '';
42252                 }
42253                 continue;
42254             }
42255             
42256             // style cleanup!?
42257             // class cleanup?
42258             
42259         }
42260         
42261         
42262         this.cleanUpChildren(node);
42263         
42264         
42265     },
42266     
42267     /**
42268      * Clean up MS wordisms...
42269      */
42270     cleanWord : function(node)
42271     {
42272         
42273         
42274         if (!node) {
42275             this.cleanWord(this.doc.body);
42276             return;
42277         }
42278         if (node.nodeName == "#text") {
42279             // clean up silly Windows -- stuff?
42280             return; 
42281         }
42282         if (node.nodeName == "#comment") {
42283             node.parentNode.removeChild(node);
42284             // clean up silly Windows -- stuff?
42285             return; 
42286         }
42287         
42288         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42289             node.parentNode.removeChild(node);
42290             return;
42291         }
42292         
42293         // remove - but keep children..
42294         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42295             while (node.childNodes.length) {
42296                 var cn = node.childNodes[0];
42297                 node.removeChild(cn);
42298                 node.parentNode.insertBefore(cn, node);
42299             }
42300             node.parentNode.removeChild(node);
42301             this.iterateChildren(node, this.cleanWord);
42302             return;
42303         }
42304         // clean styles
42305         if (node.className.length) {
42306             
42307             var cn = node.className.split(/\W+/);
42308             var cna = [];
42309             Roo.each(cn, function(cls) {
42310                 if (cls.match(/Mso[a-zA-Z]+/)) {
42311                     return;
42312                 }
42313                 cna.push(cls);
42314             });
42315             node.className = cna.length ? cna.join(' ') : '';
42316             if (!cna.length) {
42317                 node.removeAttribute("class");
42318             }
42319         }
42320         
42321         if (node.hasAttribute("lang")) {
42322             node.removeAttribute("lang");
42323         }
42324         
42325         if (node.hasAttribute("style")) {
42326             
42327             var styles = node.getAttribute("style").split(";");
42328             var nstyle = [];
42329             Roo.each(styles, function(s) {
42330                 if (!s.match(/:/)) {
42331                     return;
42332                 }
42333                 var kv = s.split(":");
42334                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42335                     return;
42336                 }
42337                 // what ever is left... we allow.
42338                 nstyle.push(s);
42339             });
42340             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42341             if (!nstyle.length) {
42342                 node.removeAttribute('style');
42343             }
42344         }
42345         this.iterateChildren(node, this.cleanWord);
42346         
42347         
42348         
42349     },
42350     /**
42351      * iterateChildren of a Node, calling fn each time, using this as the scole..
42352      * @param {DomNode} node node to iterate children of.
42353      * @param {Function} fn method of this class to call on each item.
42354      */
42355     iterateChildren : function(node, fn)
42356     {
42357         if (!node.childNodes.length) {
42358                 return;
42359         }
42360         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42361            fn.call(this, node.childNodes[i])
42362         }
42363     },
42364     
42365     
42366     /**
42367      * cleanTableWidths.
42368      *
42369      * Quite often pasting from word etc.. results in tables with column and widths.
42370      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42371      *
42372      */
42373     cleanTableWidths : function(node)
42374     {
42375          
42376          
42377         if (!node) {
42378             this.cleanTableWidths(this.doc.body);
42379             return;
42380         }
42381         
42382         // ignore list...
42383         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42384             return; 
42385         }
42386         Roo.log(node.tagName);
42387         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42388             this.iterateChildren(node, this.cleanTableWidths);
42389             return;
42390         }
42391         if (node.hasAttribute('width')) {
42392             node.removeAttribute('width');
42393         }
42394         
42395          
42396         if (node.hasAttribute("style")) {
42397             // pretty basic...
42398             
42399             var styles = node.getAttribute("style").split(";");
42400             var nstyle = [];
42401             Roo.each(styles, function(s) {
42402                 if (!s.match(/:/)) {
42403                     return;
42404                 }
42405                 var kv = s.split(":");
42406                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42407                     return;
42408                 }
42409                 // what ever is left... we allow.
42410                 nstyle.push(s);
42411             });
42412             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42413             if (!nstyle.length) {
42414                 node.removeAttribute('style');
42415             }
42416         }
42417         
42418         this.iterateChildren(node, this.cleanTableWidths);
42419         
42420         
42421     },
42422     
42423     
42424     
42425     
42426     domToHTML : function(currentElement, depth, nopadtext) {
42427         
42428         depth = depth || 0;
42429         nopadtext = nopadtext || false;
42430     
42431         if (!currentElement) {
42432             return this.domToHTML(this.doc.body);
42433         }
42434         
42435         //Roo.log(currentElement);
42436         var j;
42437         var allText = false;
42438         var nodeName = currentElement.nodeName;
42439         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42440         
42441         if  (nodeName == '#text') {
42442             
42443             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42444         }
42445         
42446         
42447         var ret = '';
42448         if (nodeName != 'BODY') {
42449              
42450             var i = 0;
42451             // Prints the node tagName, such as <A>, <IMG>, etc
42452             if (tagName) {
42453                 var attr = [];
42454                 for(i = 0; i < currentElement.attributes.length;i++) {
42455                     // quoting?
42456                     var aname = currentElement.attributes.item(i).name;
42457                     if (!currentElement.attributes.item(i).value.length) {
42458                         continue;
42459                     }
42460                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42461                 }
42462                 
42463                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42464             } 
42465             else {
42466                 
42467                 // eack
42468             }
42469         } else {
42470             tagName = false;
42471         }
42472         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42473             return ret;
42474         }
42475         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42476             nopadtext = true;
42477         }
42478         
42479         
42480         // Traverse the tree
42481         i = 0;
42482         var currentElementChild = currentElement.childNodes.item(i);
42483         var allText = true;
42484         var innerHTML  = '';
42485         lastnode = '';
42486         while (currentElementChild) {
42487             // Formatting code (indent the tree so it looks nice on the screen)
42488             var nopad = nopadtext;
42489             if (lastnode == 'SPAN') {
42490                 nopad  = true;
42491             }
42492             // text
42493             if  (currentElementChild.nodeName == '#text') {
42494                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42495                 toadd = nopadtext ? toadd : toadd.trim();
42496                 if (!nopad && toadd.length > 80) {
42497                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42498                 }
42499                 innerHTML  += toadd;
42500                 
42501                 i++;
42502                 currentElementChild = currentElement.childNodes.item(i);
42503                 lastNode = '';
42504                 continue;
42505             }
42506             allText = false;
42507             
42508             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42509                 
42510             // Recursively traverse the tree structure of the child node
42511             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42512             lastnode = currentElementChild.nodeName;
42513             i++;
42514             currentElementChild=currentElement.childNodes.item(i);
42515         }
42516         
42517         ret += innerHTML;
42518         
42519         if (!allText) {
42520                 // The remaining code is mostly for formatting the tree
42521             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42522         }
42523         
42524         
42525         if (tagName) {
42526             ret+= "</"+tagName+">";
42527         }
42528         return ret;
42529         
42530     },
42531         
42532     applyBlacklists : function()
42533     {
42534         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42535         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42536         
42537         this.white = [];
42538         this.black = [];
42539         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42540             if (b.indexOf(tag) > -1) {
42541                 return;
42542             }
42543             this.white.push(tag);
42544             
42545         }, this);
42546         
42547         Roo.each(w, function(tag) {
42548             if (b.indexOf(tag) > -1) {
42549                 return;
42550             }
42551             if (this.white.indexOf(tag) > -1) {
42552                 return;
42553             }
42554             this.white.push(tag);
42555             
42556         }, this);
42557         
42558         
42559         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42560             if (w.indexOf(tag) > -1) {
42561                 return;
42562             }
42563             this.black.push(tag);
42564             
42565         }, this);
42566         
42567         Roo.each(b, function(tag) {
42568             if (w.indexOf(tag) > -1) {
42569                 return;
42570             }
42571             if (this.black.indexOf(tag) > -1) {
42572                 return;
42573             }
42574             this.black.push(tag);
42575             
42576         }, this);
42577         
42578         
42579         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42580         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42581         
42582         this.cwhite = [];
42583         this.cblack = [];
42584         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42585             if (b.indexOf(tag) > -1) {
42586                 return;
42587             }
42588             this.cwhite.push(tag);
42589             
42590         }, this);
42591         
42592         Roo.each(w, function(tag) {
42593             if (b.indexOf(tag) > -1) {
42594                 return;
42595             }
42596             if (this.cwhite.indexOf(tag) > -1) {
42597                 return;
42598             }
42599             this.cwhite.push(tag);
42600             
42601         }, this);
42602         
42603         
42604         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42605             if (w.indexOf(tag) > -1) {
42606                 return;
42607             }
42608             this.cblack.push(tag);
42609             
42610         }, this);
42611         
42612         Roo.each(b, function(tag) {
42613             if (w.indexOf(tag) > -1) {
42614                 return;
42615             }
42616             if (this.cblack.indexOf(tag) > -1) {
42617                 return;
42618             }
42619             this.cblack.push(tag);
42620             
42621         }, this);
42622     },
42623     
42624     setStylesheets : function(stylesheets)
42625     {
42626         if(typeof(stylesheets) == 'string'){
42627             Roo.get(this.iframe.contentDocument.head).createChild({
42628                 tag : 'link',
42629                 rel : 'stylesheet',
42630                 type : 'text/css',
42631                 href : stylesheets
42632             });
42633             
42634             return;
42635         }
42636         var _this = this;
42637      
42638         Roo.each(stylesheets, function(s) {
42639             if(!s.length){
42640                 return;
42641             }
42642             
42643             Roo.get(_this.iframe.contentDocument.head).createChild({
42644                 tag : 'link',
42645                 rel : 'stylesheet',
42646                 type : 'text/css',
42647                 href : s
42648             });
42649         });
42650
42651         
42652     },
42653     
42654     removeStylesheets : function()
42655     {
42656         var _this = this;
42657         
42658         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42659             s.remove();
42660         });
42661     }
42662     
42663     // hide stuff that is not compatible
42664     /**
42665      * @event blur
42666      * @hide
42667      */
42668     /**
42669      * @event change
42670      * @hide
42671      */
42672     /**
42673      * @event focus
42674      * @hide
42675      */
42676     /**
42677      * @event specialkey
42678      * @hide
42679      */
42680     /**
42681      * @cfg {String} fieldClass @hide
42682      */
42683     /**
42684      * @cfg {String} focusClass @hide
42685      */
42686     /**
42687      * @cfg {String} autoCreate @hide
42688      */
42689     /**
42690      * @cfg {String} inputType @hide
42691      */
42692     /**
42693      * @cfg {String} invalidClass @hide
42694      */
42695     /**
42696      * @cfg {String} invalidText @hide
42697      */
42698     /**
42699      * @cfg {String} msgFx @hide
42700      */
42701     /**
42702      * @cfg {String} validateOnBlur @hide
42703      */
42704 });
42705
42706 Roo.HtmlEditorCore.white = [
42707         'area', 'br', 'img', 'input', 'hr', 'wbr',
42708         
42709        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42710        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42711        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42712        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42713        'table',   'ul',         'xmp', 
42714        
42715        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42716       'thead',   'tr', 
42717      
42718       'dir', 'menu', 'ol', 'ul', 'dl',
42719        
42720       'embed',  'object'
42721 ];
42722
42723
42724 Roo.HtmlEditorCore.black = [
42725     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42726         'applet', // 
42727         'base',   'basefont', 'bgsound', 'blink',  'body', 
42728         'frame',  'frameset', 'head',    'html',   'ilayer', 
42729         'iframe', 'layer',  'link',     'meta',    'object',   
42730         'script', 'style' ,'title',  'xml' // clean later..
42731 ];
42732 Roo.HtmlEditorCore.clean = [
42733     'script', 'style', 'title', 'xml'
42734 ];
42735 Roo.HtmlEditorCore.remove = [
42736     'font'
42737 ];
42738 // attributes..
42739
42740 Roo.HtmlEditorCore.ablack = [
42741     'on'
42742 ];
42743     
42744 Roo.HtmlEditorCore.aclean = [ 
42745     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42746 ];
42747
42748 // protocols..
42749 Roo.HtmlEditorCore.pwhite= [
42750         'http',  'https',  'mailto'
42751 ];
42752
42753 // white listed style attributes.
42754 Roo.HtmlEditorCore.cwhite= [
42755       //  'text-align', /// default is to allow most things..
42756       
42757          
42758 //        'font-size'//??
42759 ];
42760
42761 // black listed style attributes.
42762 Roo.HtmlEditorCore.cblack= [
42763       //  'font-size' -- this can be set by the project 
42764 ];
42765
42766
42767 Roo.HtmlEditorCore.swapCodes   =[ 
42768     [    8211, "--" ], 
42769     [    8212, "--" ], 
42770     [    8216,  "'" ],  
42771     [    8217, "'" ],  
42772     [    8220, '"' ],  
42773     [    8221, '"' ],  
42774     [    8226, "*" ],  
42775     [    8230, "..." ]
42776 ]; 
42777
42778     //<script type="text/javascript">
42779
42780 /*
42781  * Ext JS Library 1.1.1
42782  * Copyright(c) 2006-2007, Ext JS, LLC.
42783  * Licence LGPL
42784  * 
42785  */
42786  
42787  
42788 Roo.form.HtmlEditor = function(config){
42789     
42790     
42791     
42792     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42793     
42794     if (!this.toolbars) {
42795         this.toolbars = [];
42796     }
42797     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42798     
42799     
42800 };
42801
42802 /**
42803  * @class Roo.form.HtmlEditor
42804  * @extends Roo.form.Field
42805  * Provides a lightweight HTML Editor component.
42806  *
42807  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42808  * 
42809  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42810  * supported by this editor.</b><br/><br/>
42811  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42812  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42813  */
42814 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42815     /**
42816      * @cfg {Boolean} clearUp
42817      */
42818     clearUp : true,
42819       /**
42820      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42821      */
42822     toolbars : false,
42823    
42824      /**
42825      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42826      *                        Roo.resizable.
42827      */
42828     resizable : false,
42829      /**
42830      * @cfg {Number} height (in pixels)
42831      */   
42832     height: 300,
42833    /**
42834      * @cfg {Number} width (in pixels)
42835      */   
42836     width: 500,
42837     
42838     /**
42839      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42840      * 
42841      */
42842     stylesheets: false,
42843     
42844     
42845      /**
42846      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42847      * 
42848      */
42849     cblack: false,
42850     /**
42851      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42852      * 
42853      */
42854     cwhite: false,
42855     
42856      /**
42857      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42858      * 
42859      */
42860     black: false,
42861     /**
42862      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42863      * 
42864      */
42865     white: false,
42866     
42867     // id of frame..
42868     frameId: false,
42869     
42870     // private properties
42871     validationEvent : false,
42872     deferHeight: true,
42873     initialized : false,
42874     activated : false,
42875     
42876     onFocus : Roo.emptyFn,
42877     iframePad:3,
42878     hideMode:'offsets',
42879     
42880     actionMode : 'container', // defaults to hiding it...
42881     
42882     defaultAutoCreate : { // modified by initCompnoent..
42883         tag: "textarea",
42884         style:"width:500px;height:300px;",
42885         autocomplete: "new-password"
42886     },
42887
42888     // private
42889     initComponent : function(){
42890         this.addEvents({
42891             /**
42892              * @event initialize
42893              * Fires when the editor is fully initialized (including the iframe)
42894              * @param {HtmlEditor} this
42895              */
42896             initialize: true,
42897             /**
42898              * @event activate
42899              * Fires when the editor is first receives the focus. Any insertion must wait
42900              * until after this event.
42901              * @param {HtmlEditor} this
42902              */
42903             activate: true,
42904              /**
42905              * @event beforesync
42906              * Fires before the textarea is updated with content from the editor iframe. Return false
42907              * to cancel the sync.
42908              * @param {HtmlEditor} this
42909              * @param {String} html
42910              */
42911             beforesync: true,
42912              /**
42913              * @event beforepush
42914              * Fires before the iframe editor is updated with content from the textarea. Return false
42915              * to cancel the push.
42916              * @param {HtmlEditor} this
42917              * @param {String} html
42918              */
42919             beforepush: true,
42920              /**
42921              * @event sync
42922              * Fires when the textarea is updated with content from the editor iframe.
42923              * @param {HtmlEditor} this
42924              * @param {String} html
42925              */
42926             sync: true,
42927              /**
42928              * @event push
42929              * Fires when the iframe editor is updated with content from the textarea.
42930              * @param {HtmlEditor} this
42931              * @param {String} html
42932              */
42933             push: true,
42934              /**
42935              * @event editmodechange
42936              * Fires when the editor switches edit modes
42937              * @param {HtmlEditor} this
42938              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42939              */
42940             editmodechange: true,
42941             /**
42942              * @event editorevent
42943              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42944              * @param {HtmlEditor} this
42945              */
42946             editorevent: true,
42947             /**
42948              * @event firstfocus
42949              * Fires when on first focus - needed by toolbars..
42950              * @param {HtmlEditor} this
42951              */
42952             firstfocus: true,
42953             /**
42954              * @event autosave
42955              * Auto save the htmlEditor value as a file into Events
42956              * @param {HtmlEditor} this
42957              */
42958             autosave: true,
42959             /**
42960              * @event savedpreview
42961              * preview the saved version of htmlEditor
42962              * @param {HtmlEditor} this
42963              */
42964             savedpreview: true,
42965             
42966             /**
42967             * @event stylesheetsclick
42968             * Fires when press the Sytlesheets button
42969             * @param {Roo.HtmlEditorCore} this
42970             */
42971             stylesheetsclick: true
42972         });
42973         this.defaultAutoCreate =  {
42974             tag: "textarea",
42975             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42976             autocomplete: "new-password"
42977         };
42978     },
42979
42980     /**
42981      * Protected method that will not generally be called directly. It
42982      * is called when the editor creates its toolbar. Override this method if you need to
42983      * add custom toolbar buttons.
42984      * @param {HtmlEditor} editor
42985      */
42986     createToolbar : function(editor){
42987         Roo.log("create toolbars");
42988         if (!editor.toolbars || !editor.toolbars.length) {
42989             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42990         }
42991         
42992         for (var i =0 ; i < editor.toolbars.length;i++) {
42993             editor.toolbars[i] = Roo.factory(
42994                     typeof(editor.toolbars[i]) == 'string' ?
42995                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42996                 Roo.form.HtmlEditor);
42997             editor.toolbars[i].init(editor);
42998         }
42999          
43000         
43001     },
43002
43003      
43004     // private
43005     onRender : function(ct, position)
43006     {
43007         var _t = this;
43008         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
43009         
43010         this.wrap = this.el.wrap({
43011             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43012         });
43013         
43014         this.editorcore.onRender(ct, position);
43015          
43016         if (this.resizable) {
43017             this.resizeEl = new Roo.Resizable(this.wrap, {
43018                 pinned : true,
43019                 wrap: true,
43020                 dynamic : true,
43021                 minHeight : this.height,
43022                 height: this.height,
43023                 handles : this.resizable,
43024                 width: this.width,
43025                 listeners : {
43026                     resize : function(r, w, h) {
43027                         _t.onResize(w,h); // -something
43028                     }
43029                 }
43030             });
43031             
43032         }
43033         this.createToolbar(this);
43034        
43035         
43036         if(!this.width){
43037             this.setSize(this.wrap.getSize());
43038         }
43039         if (this.resizeEl) {
43040             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43041             // should trigger onReize..
43042         }
43043         
43044         this.keyNav = new Roo.KeyNav(this.el, {
43045             
43046             "tab" : function(e){
43047                 e.preventDefault();
43048                 
43049                 var value = this.getValue();
43050                 
43051                 var start = this.el.dom.selectionStart;
43052                 var end = this.el.dom.selectionEnd;
43053                 
43054                 if(!e.shiftKey){
43055                     
43056                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43057                     this.el.dom.setSelectionRange(end + 1, end + 1);
43058                     return;
43059                 }
43060                 
43061                 var f = value.substring(0, start).split("\t");
43062                 
43063                 if(f.pop().length != 0){
43064                     return;
43065                 }
43066                 
43067                 this.setValue(f.join("\t") + value.substring(end));
43068                 this.el.dom.setSelectionRange(start - 1, start - 1);
43069                 
43070             },
43071             
43072             "home" : function(e){
43073                 e.preventDefault();
43074                 
43075                 var curr = this.el.dom.selectionStart;
43076                 var lines = this.getValue().split("\n");
43077                 
43078                 if(!lines.length){
43079                     return;
43080                 }
43081                 
43082                 if(e.ctrlKey){
43083                     this.el.dom.setSelectionRange(0, 0);
43084                     return;
43085                 }
43086                 
43087                 var pos = 0;
43088                 
43089                 for (var i = 0; i < lines.length;i++) {
43090                     pos += lines[i].length;
43091                     
43092                     if(i != 0){
43093                         pos += 1;
43094                     }
43095                     
43096                     if(pos < curr){
43097                         continue;
43098                     }
43099                     
43100                     pos -= lines[i].length;
43101                     
43102                     break;
43103                 }
43104                 
43105                 if(!e.shiftKey){
43106                     this.el.dom.setSelectionRange(pos, pos);
43107                     return;
43108                 }
43109                 
43110                 this.el.dom.selectionStart = pos;
43111                 this.el.dom.selectionEnd = curr;
43112             },
43113             
43114             "end" : function(e){
43115                 e.preventDefault();
43116                 
43117                 var curr = this.el.dom.selectionStart;
43118                 var lines = this.getValue().split("\n");
43119                 
43120                 if(!lines.length){
43121                     return;
43122                 }
43123                 
43124                 if(e.ctrlKey){
43125                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43126                     return;
43127                 }
43128                 
43129                 var pos = 0;
43130                 
43131                 for (var i = 0; i < lines.length;i++) {
43132                     
43133                     pos += lines[i].length;
43134                     
43135                     if(i != 0){
43136                         pos += 1;
43137                     }
43138                     
43139                     if(pos < curr){
43140                         continue;
43141                     }
43142                     
43143                     break;
43144                 }
43145                 
43146                 if(!e.shiftKey){
43147                     this.el.dom.setSelectionRange(pos, pos);
43148                     return;
43149                 }
43150                 
43151                 this.el.dom.selectionStart = curr;
43152                 this.el.dom.selectionEnd = pos;
43153             },
43154
43155             scope : this,
43156
43157             doRelay : function(foo, bar, hname){
43158                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43159             },
43160
43161             forceKeyDown: true
43162         });
43163         
43164 //        if(this.autosave && this.w){
43165 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43166 //        }
43167     },
43168
43169     // private
43170     onResize : function(w, h)
43171     {
43172         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43173         var ew = false;
43174         var eh = false;
43175         
43176         if(this.el ){
43177             if(typeof w == 'number'){
43178                 var aw = w - this.wrap.getFrameWidth('lr');
43179                 this.el.setWidth(this.adjustWidth('textarea', aw));
43180                 ew = aw;
43181             }
43182             if(typeof h == 'number'){
43183                 var tbh = 0;
43184                 for (var i =0; i < this.toolbars.length;i++) {
43185                     // fixme - ask toolbars for heights?
43186                     tbh += this.toolbars[i].tb.el.getHeight();
43187                     if (this.toolbars[i].footer) {
43188                         tbh += this.toolbars[i].footer.el.getHeight();
43189                     }
43190                 }
43191                 
43192                 
43193                 
43194                 
43195                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43196                 ah -= 5; // knock a few pixes off for look..
43197 //                Roo.log(ah);
43198                 this.el.setHeight(this.adjustWidth('textarea', ah));
43199                 var eh = ah;
43200             }
43201         }
43202         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43203         this.editorcore.onResize(ew,eh);
43204         
43205     },
43206
43207     /**
43208      * Toggles the editor between standard and source edit mode.
43209      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43210      */
43211     toggleSourceEdit : function(sourceEditMode)
43212     {
43213         this.editorcore.toggleSourceEdit(sourceEditMode);
43214         
43215         if(this.editorcore.sourceEditMode){
43216             Roo.log('editor - showing textarea');
43217             
43218 //            Roo.log('in');
43219 //            Roo.log(this.syncValue());
43220             this.editorcore.syncValue();
43221             this.el.removeClass('x-hidden');
43222             this.el.dom.removeAttribute('tabIndex');
43223             this.el.focus();
43224             
43225             for (var i = 0; i < this.toolbars.length; i++) {
43226                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43227                     this.toolbars[i].tb.hide();
43228                     this.toolbars[i].footer.hide();
43229                 }
43230             }
43231             
43232         }else{
43233             Roo.log('editor - hiding textarea');
43234 //            Roo.log('out')
43235 //            Roo.log(this.pushValue()); 
43236             this.editorcore.pushValue();
43237             
43238             this.el.addClass('x-hidden');
43239             this.el.dom.setAttribute('tabIndex', -1);
43240             
43241             for (var i = 0; i < this.toolbars.length; i++) {
43242                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43243                     this.toolbars[i].tb.show();
43244                     this.toolbars[i].footer.show();
43245                 }
43246             }
43247             
43248             //this.deferFocus();
43249         }
43250         
43251         this.setSize(this.wrap.getSize());
43252         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43253         
43254         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43255     },
43256  
43257     // private (for BoxComponent)
43258     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43259
43260     // private (for BoxComponent)
43261     getResizeEl : function(){
43262         return this.wrap;
43263     },
43264
43265     // private (for BoxComponent)
43266     getPositionEl : function(){
43267         return this.wrap;
43268     },
43269
43270     // private
43271     initEvents : function(){
43272         this.originalValue = this.getValue();
43273     },
43274
43275     /**
43276      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43277      * @method
43278      */
43279     markInvalid : Roo.emptyFn,
43280     /**
43281      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43282      * @method
43283      */
43284     clearInvalid : Roo.emptyFn,
43285
43286     setValue : function(v){
43287         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43288         this.editorcore.pushValue();
43289     },
43290
43291      
43292     // private
43293     deferFocus : function(){
43294         this.focus.defer(10, this);
43295     },
43296
43297     // doc'ed in Field
43298     focus : function(){
43299         this.editorcore.focus();
43300         
43301     },
43302       
43303
43304     // private
43305     onDestroy : function(){
43306         
43307         
43308         
43309         if(this.rendered){
43310             
43311             for (var i =0; i < this.toolbars.length;i++) {
43312                 // fixme - ask toolbars for heights?
43313                 this.toolbars[i].onDestroy();
43314             }
43315             
43316             this.wrap.dom.innerHTML = '';
43317             this.wrap.remove();
43318         }
43319     },
43320
43321     // private
43322     onFirstFocus : function(){
43323         //Roo.log("onFirstFocus");
43324         this.editorcore.onFirstFocus();
43325          for (var i =0; i < this.toolbars.length;i++) {
43326             this.toolbars[i].onFirstFocus();
43327         }
43328         
43329     },
43330     
43331     // private
43332     syncValue : function()
43333     {
43334         this.editorcore.syncValue();
43335     },
43336     
43337     pushValue : function()
43338     {
43339         this.editorcore.pushValue();
43340     },
43341     
43342     setStylesheets : function(stylesheets)
43343     {
43344         this.editorcore.setStylesheets(stylesheets);
43345     },
43346     
43347     removeStylesheets : function()
43348     {
43349         this.editorcore.removeStylesheets();
43350     }
43351      
43352     
43353     // hide stuff that is not compatible
43354     /**
43355      * @event blur
43356      * @hide
43357      */
43358     /**
43359      * @event change
43360      * @hide
43361      */
43362     /**
43363      * @event focus
43364      * @hide
43365      */
43366     /**
43367      * @event specialkey
43368      * @hide
43369      */
43370     /**
43371      * @cfg {String} fieldClass @hide
43372      */
43373     /**
43374      * @cfg {String} focusClass @hide
43375      */
43376     /**
43377      * @cfg {String} autoCreate @hide
43378      */
43379     /**
43380      * @cfg {String} inputType @hide
43381      */
43382     /**
43383      * @cfg {String} invalidClass @hide
43384      */
43385     /**
43386      * @cfg {String} invalidText @hide
43387      */
43388     /**
43389      * @cfg {String} msgFx @hide
43390      */
43391     /**
43392      * @cfg {String} validateOnBlur @hide
43393      */
43394 });
43395  
43396     // <script type="text/javascript">
43397 /*
43398  * Based on
43399  * Ext JS Library 1.1.1
43400  * Copyright(c) 2006-2007, Ext JS, LLC.
43401  *  
43402  
43403  */
43404
43405 /**
43406  * @class Roo.form.HtmlEditorToolbar1
43407  * Basic Toolbar
43408  * 
43409  * Usage:
43410  *
43411  new Roo.form.HtmlEditor({
43412     ....
43413     toolbars : [
43414         new Roo.form.HtmlEditorToolbar1({
43415             disable : { fonts: 1 , format: 1, ..., ... , ...],
43416             btns : [ .... ]
43417         })
43418     }
43419      
43420  * 
43421  * @cfg {Object} disable List of elements to disable..
43422  * @cfg {Array} btns List of additional buttons.
43423  * 
43424  * 
43425  * NEEDS Extra CSS? 
43426  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43427  */
43428  
43429 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43430 {
43431     
43432     Roo.apply(this, config);
43433     
43434     // default disabled, based on 'good practice'..
43435     this.disable = this.disable || {};
43436     Roo.applyIf(this.disable, {
43437         fontSize : true,
43438         colors : true,
43439         specialElements : true
43440     });
43441     
43442     
43443     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43444     // dont call parent... till later.
43445 }
43446
43447 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43448     
43449     tb: false,
43450     
43451     rendered: false,
43452     
43453     editor : false,
43454     editorcore : false,
43455     /**
43456      * @cfg {Object} disable  List of toolbar elements to disable
43457          
43458      */
43459     disable : false,
43460     
43461     
43462      /**
43463      * @cfg {String} createLinkText The default text for the create link prompt
43464      */
43465     createLinkText : 'Please enter the URL for the link:',
43466     /**
43467      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43468      */
43469     defaultLinkValue : 'http:/'+'/',
43470    
43471     
43472       /**
43473      * @cfg {Array} fontFamilies An array of available font families
43474      */
43475     fontFamilies : [
43476         'Arial',
43477         'Courier New',
43478         'Tahoma',
43479         'Times New Roman',
43480         'Verdana'
43481     ],
43482     
43483     specialChars : [
43484            "&#169;",
43485           "&#174;",     
43486           "&#8482;",    
43487           "&#163;" ,    
43488          // "&#8212;",    
43489           "&#8230;",    
43490           "&#247;" ,    
43491         //  "&#225;" ,     ?? a acute?
43492            "&#8364;"    , //Euro
43493        //   "&#8220;"    ,
43494         //  "&#8221;"    ,
43495         //  "&#8226;"    ,
43496           "&#176;"  //   , // degrees
43497
43498          // "&#233;"     , // e ecute
43499          // "&#250;"     , // u ecute?
43500     ],
43501     
43502     specialElements : [
43503         {
43504             text: "Insert Table",
43505             xtype: 'MenuItem',
43506             xns : Roo.Menu,
43507             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43508                 
43509         },
43510         {    
43511             text: "Insert Image",
43512             xtype: 'MenuItem',
43513             xns : Roo.Menu,
43514             ihtml : '<img src="about:blank"/>'
43515             
43516         }
43517         
43518          
43519     ],
43520     
43521     
43522     inputElements : [ 
43523             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43524             "input:submit", "input:button", "select", "textarea", "label" ],
43525     formats : [
43526         ["p"] ,  
43527         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43528         ["pre"],[ "code"], 
43529         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43530         ['div'],['span']
43531     ],
43532     
43533     cleanStyles : [
43534         "font-size"
43535     ],
43536      /**
43537      * @cfg {String} defaultFont default font to use.
43538      */
43539     defaultFont: 'tahoma',
43540    
43541     fontSelect : false,
43542     
43543     
43544     formatCombo : false,
43545     
43546     init : function(editor)
43547     {
43548         this.editor = editor;
43549         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43550         var editorcore = this.editorcore;
43551         
43552         var _t = this;
43553         
43554         var fid = editorcore.frameId;
43555         var etb = this;
43556         function btn(id, toggle, handler){
43557             var xid = fid + '-'+ id ;
43558             return {
43559                 id : xid,
43560                 cmd : id,
43561                 cls : 'x-btn-icon x-edit-'+id,
43562                 enableToggle:toggle !== false,
43563                 scope: _t, // was editor...
43564                 handler:handler||_t.relayBtnCmd,
43565                 clickEvent:'mousedown',
43566                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43567                 tabIndex:-1
43568             };
43569         }
43570         
43571         
43572         
43573         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43574         this.tb = tb;
43575          // stop form submits
43576         tb.el.on('click', function(e){
43577             e.preventDefault(); // what does this do?
43578         });
43579
43580         if(!this.disable.font) { // && !Roo.isSafari){
43581             /* why no safari for fonts 
43582             editor.fontSelect = tb.el.createChild({
43583                 tag:'select',
43584                 tabIndex: -1,
43585                 cls:'x-font-select',
43586                 html: this.createFontOptions()
43587             });
43588             
43589             editor.fontSelect.on('change', function(){
43590                 var font = editor.fontSelect.dom.value;
43591                 editor.relayCmd('fontname', font);
43592                 editor.deferFocus();
43593             }, editor);
43594             
43595             tb.add(
43596                 editor.fontSelect.dom,
43597                 '-'
43598             );
43599             */
43600             
43601         };
43602         if(!this.disable.formats){
43603             this.formatCombo = new Roo.form.ComboBox({
43604                 store: new Roo.data.SimpleStore({
43605                     id : 'tag',
43606                     fields: ['tag'],
43607                     data : this.formats // from states.js
43608                 }),
43609                 blockFocus : true,
43610                 name : '',
43611                 //autoCreate : {tag: "div",  size: "20"},
43612                 displayField:'tag',
43613                 typeAhead: false,
43614                 mode: 'local',
43615                 editable : false,
43616                 triggerAction: 'all',
43617                 emptyText:'Add tag',
43618                 selectOnFocus:true,
43619                 width:135,
43620                 listeners : {
43621                     'select': function(c, r, i) {
43622                         editorcore.insertTag(r.get('tag'));
43623                         editor.focus();
43624                     }
43625                 }
43626
43627             });
43628             tb.addField(this.formatCombo);
43629             
43630         }
43631         
43632         if(!this.disable.format){
43633             tb.add(
43634                 btn('bold'),
43635                 btn('italic'),
43636                 btn('underline'),
43637                 btn('strikethrough')
43638             );
43639         };
43640         if(!this.disable.fontSize){
43641             tb.add(
43642                 '-',
43643                 
43644                 
43645                 btn('increasefontsize', false, editorcore.adjustFont),
43646                 btn('decreasefontsize', false, editorcore.adjustFont)
43647             );
43648         };
43649         
43650         
43651         if(!this.disable.colors){
43652             tb.add(
43653                 '-', {
43654                     id:editorcore.frameId +'-forecolor',
43655                     cls:'x-btn-icon x-edit-forecolor',
43656                     clickEvent:'mousedown',
43657                     tooltip: this.buttonTips['forecolor'] || undefined,
43658                     tabIndex:-1,
43659                     menu : new Roo.menu.ColorMenu({
43660                         allowReselect: true,
43661                         focus: Roo.emptyFn,
43662                         value:'000000',
43663                         plain:true,
43664                         selectHandler: function(cp, color){
43665                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43666                             editor.deferFocus();
43667                         },
43668                         scope: editorcore,
43669                         clickEvent:'mousedown'
43670                     })
43671                 }, {
43672                     id:editorcore.frameId +'backcolor',
43673                     cls:'x-btn-icon x-edit-backcolor',
43674                     clickEvent:'mousedown',
43675                     tooltip: this.buttonTips['backcolor'] || undefined,
43676                     tabIndex:-1,
43677                     menu : new Roo.menu.ColorMenu({
43678                         focus: Roo.emptyFn,
43679                         value:'FFFFFF',
43680                         plain:true,
43681                         allowReselect: true,
43682                         selectHandler: function(cp, color){
43683                             if(Roo.isGecko){
43684                                 editorcore.execCmd('useCSS', false);
43685                                 editorcore.execCmd('hilitecolor', color);
43686                                 editorcore.execCmd('useCSS', true);
43687                                 editor.deferFocus();
43688                             }else{
43689                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43690                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43691                                 editor.deferFocus();
43692                             }
43693                         },
43694                         scope:editorcore,
43695                         clickEvent:'mousedown'
43696                     })
43697                 }
43698             );
43699         };
43700         // now add all the items...
43701         
43702
43703         if(!this.disable.alignments){
43704             tb.add(
43705                 '-',
43706                 btn('justifyleft'),
43707                 btn('justifycenter'),
43708                 btn('justifyright')
43709             );
43710         };
43711
43712         //if(!Roo.isSafari){
43713             if(!this.disable.links){
43714                 tb.add(
43715                     '-',
43716                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43717                 );
43718             };
43719
43720             if(!this.disable.lists){
43721                 tb.add(
43722                     '-',
43723                     btn('insertorderedlist'),
43724                     btn('insertunorderedlist')
43725                 );
43726             }
43727             if(!this.disable.sourceEdit){
43728                 tb.add(
43729                     '-',
43730                     btn('sourceedit', true, function(btn){
43731                         this.toggleSourceEdit(btn.pressed);
43732                     })
43733                 );
43734             }
43735         //}
43736         
43737         var smenu = { };
43738         // special menu.. - needs to be tidied up..
43739         if (!this.disable.special) {
43740             smenu = {
43741                 text: "&#169;",
43742                 cls: 'x-edit-none',
43743                 
43744                 menu : {
43745                     items : []
43746                 }
43747             };
43748             for (var i =0; i < this.specialChars.length; i++) {
43749                 smenu.menu.items.push({
43750                     
43751                     html: this.specialChars[i],
43752                     handler: function(a,b) {
43753                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43754                         //editor.insertAtCursor(a.html);
43755                         
43756                     },
43757                     tabIndex:-1
43758                 });
43759             }
43760             
43761             
43762             tb.add(smenu);
43763             
43764             
43765         }
43766         
43767         var cmenu = { };
43768         if (!this.disable.cleanStyles) {
43769             cmenu = {
43770                 cls: 'x-btn-icon x-btn-clear',
43771                 
43772                 menu : {
43773                     items : []
43774                 }
43775             };
43776             for (var i =0; i < this.cleanStyles.length; i++) {
43777                 cmenu.menu.items.push({
43778                     actiontype : this.cleanStyles[i],
43779                     html: 'Remove ' + this.cleanStyles[i],
43780                     handler: function(a,b) {
43781 //                        Roo.log(a);
43782 //                        Roo.log(b);
43783                         var c = Roo.get(editorcore.doc.body);
43784                         c.select('[style]').each(function(s) {
43785                             s.dom.style.removeProperty(a.actiontype);
43786                         });
43787                         editorcore.syncValue();
43788                     },
43789                     tabIndex:-1
43790                 });
43791             }
43792              cmenu.menu.items.push({
43793                 actiontype : 'tablewidths',
43794                 html: 'Remove Table Widths',
43795                 handler: function(a,b) {
43796                     editorcore.cleanTableWidths();
43797                     editorcore.syncValue();
43798                 },
43799                 tabIndex:-1
43800             });
43801             cmenu.menu.items.push({
43802                 actiontype : 'word',
43803                 html: 'Remove MS Word Formating',
43804                 handler: function(a,b) {
43805                     editorcore.cleanWord();
43806                     editorcore.syncValue();
43807                 },
43808                 tabIndex:-1
43809             });
43810             
43811             cmenu.menu.items.push({
43812                 actiontype : 'all',
43813                 html: 'Remove All Styles',
43814                 handler: function(a,b) {
43815                     
43816                     var c = Roo.get(editorcore.doc.body);
43817                     c.select('[style]').each(function(s) {
43818                         s.dom.removeAttribute('style');
43819                     });
43820                     editorcore.syncValue();
43821                 },
43822                 tabIndex:-1
43823             });
43824             
43825             cmenu.menu.items.push({
43826                 actiontype : 'all',
43827                 html: 'Remove All CSS Classes',
43828                 handler: function(a,b) {
43829                     
43830                     var c = Roo.get(editorcore.doc.body);
43831                     c.select('[class]').each(function(s) {
43832                         s.dom.className = '';
43833                     });
43834                     editorcore.syncValue();
43835                 },
43836                 tabIndex:-1
43837             });
43838             
43839              cmenu.menu.items.push({
43840                 actiontype : 'tidy',
43841                 html: 'Tidy HTML Source',
43842                 handler: function(a,b) {
43843                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43844                     editorcore.syncValue();
43845                 },
43846                 tabIndex:-1
43847             });
43848             
43849             
43850             tb.add(cmenu);
43851         }
43852          
43853         if (!this.disable.specialElements) {
43854             var semenu = {
43855                 text: "Other;",
43856                 cls: 'x-edit-none',
43857                 menu : {
43858                     items : []
43859                 }
43860             };
43861             for (var i =0; i < this.specialElements.length; i++) {
43862                 semenu.menu.items.push(
43863                     Roo.apply({ 
43864                         handler: function(a,b) {
43865                             editor.insertAtCursor(this.ihtml);
43866                         }
43867                     }, this.specialElements[i])
43868                 );
43869                     
43870             }
43871             
43872             tb.add(semenu);
43873             
43874             
43875         }
43876          
43877         
43878         if (this.btns) {
43879             for(var i =0; i< this.btns.length;i++) {
43880                 var b = Roo.factory(this.btns[i],Roo.form);
43881                 b.cls =  'x-edit-none';
43882                 
43883                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43884                     b.cls += ' x-init-enable';
43885                 }
43886                 
43887                 b.scope = editorcore;
43888                 tb.add(b);
43889             }
43890         
43891         }
43892         
43893         
43894         
43895         // disable everything...
43896         
43897         this.tb.items.each(function(item){
43898             
43899            if(
43900                 item.id != editorcore.frameId+ '-sourceedit' && 
43901                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43902             ){
43903                 
43904                 item.disable();
43905             }
43906         });
43907         this.rendered = true;
43908         
43909         // the all the btns;
43910         editor.on('editorevent', this.updateToolbar, this);
43911         // other toolbars need to implement this..
43912         //editor.on('editmodechange', this.updateToolbar, this);
43913     },
43914     
43915     
43916     relayBtnCmd : function(btn) {
43917         this.editorcore.relayCmd(btn.cmd);
43918     },
43919     // private used internally
43920     createLink : function(){
43921         Roo.log("create link?");
43922         var url = prompt(this.createLinkText, this.defaultLinkValue);
43923         if(url && url != 'http:/'+'/'){
43924             this.editorcore.relayCmd('createlink', url);
43925         }
43926     },
43927
43928     
43929     /**
43930      * Protected method that will not generally be called directly. It triggers
43931      * a toolbar update by reading the markup state of the current selection in the editor.
43932      */
43933     updateToolbar: function(){
43934
43935         if(!this.editorcore.activated){
43936             this.editor.onFirstFocus();
43937             return;
43938         }
43939
43940         var btns = this.tb.items.map, 
43941             doc = this.editorcore.doc,
43942             frameId = this.editorcore.frameId;
43943
43944         if(!this.disable.font && !Roo.isSafari){
43945             /*
43946             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43947             if(name != this.fontSelect.dom.value){
43948                 this.fontSelect.dom.value = name;
43949             }
43950             */
43951         }
43952         if(!this.disable.format){
43953             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43954             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43955             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43956             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
43957         }
43958         if(!this.disable.alignments){
43959             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43960             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43961             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43962         }
43963         if(!Roo.isSafari && !this.disable.lists){
43964             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43965             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43966         }
43967         
43968         var ans = this.editorcore.getAllAncestors();
43969         if (this.formatCombo) {
43970             
43971             
43972             var store = this.formatCombo.store;
43973             this.formatCombo.setValue("");
43974             for (var i =0; i < ans.length;i++) {
43975                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43976                     // select it..
43977                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43978                     break;
43979                 }
43980             }
43981         }
43982         
43983         
43984         
43985         // hides menus... - so this cant be on a menu...
43986         Roo.menu.MenuMgr.hideAll();
43987
43988         //this.editorsyncValue();
43989     },
43990    
43991     
43992     createFontOptions : function(){
43993         var buf = [], fs = this.fontFamilies, ff, lc;
43994         
43995         
43996         
43997         for(var i = 0, len = fs.length; i< len; i++){
43998             ff = fs[i];
43999             lc = ff.toLowerCase();
44000             buf.push(
44001                 '<option value="',lc,'" style="font-family:',ff,';"',
44002                     (this.defaultFont == lc ? ' selected="true">' : '>'),
44003                     ff,
44004                 '</option>'
44005             );
44006         }
44007         return buf.join('');
44008     },
44009     
44010     toggleSourceEdit : function(sourceEditMode){
44011         
44012         Roo.log("toolbar toogle");
44013         if(sourceEditMode === undefined){
44014             sourceEditMode = !this.sourceEditMode;
44015         }
44016         this.sourceEditMode = sourceEditMode === true;
44017         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44018         // just toggle the button?
44019         if(btn.pressed !== this.sourceEditMode){
44020             btn.toggle(this.sourceEditMode);
44021             return;
44022         }
44023         
44024         if(sourceEditMode){
44025             Roo.log("disabling buttons");
44026             this.tb.items.each(function(item){
44027                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44028                     item.disable();
44029                 }
44030             });
44031           
44032         }else{
44033             Roo.log("enabling buttons");
44034             if(this.editorcore.initialized){
44035                 this.tb.items.each(function(item){
44036                     item.enable();
44037                 });
44038             }
44039             
44040         }
44041         Roo.log("calling toggole on editor");
44042         // tell the editor that it's been pressed..
44043         this.editor.toggleSourceEdit(sourceEditMode);
44044        
44045     },
44046      /**
44047      * Object collection of toolbar tooltips for the buttons in the editor. The key
44048      * is the command id associated with that button and the value is a valid QuickTips object.
44049      * For example:
44050 <pre><code>
44051 {
44052     bold : {
44053         title: 'Bold (Ctrl+B)',
44054         text: 'Make the selected text bold.',
44055         cls: 'x-html-editor-tip'
44056     },
44057     italic : {
44058         title: 'Italic (Ctrl+I)',
44059         text: 'Make the selected text italic.',
44060         cls: 'x-html-editor-tip'
44061     },
44062     ...
44063 </code></pre>
44064     * @type Object
44065      */
44066     buttonTips : {
44067         bold : {
44068             title: 'Bold (Ctrl+B)',
44069             text: 'Make the selected text bold.',
44070             cls: 'x-html-editor-tip'
44071         },
44072         italic : {
44073             title: 'Italic (Ctrl+I)',
44074             text: 'Make the selected text italic.',
44075             cls: 'x-html-editor-tip'
44076         },
44077         underline : {
44078             title: 'Underline (Ctrl+U)',
44079             text: 'Underline the selected text.',
44080             cls: 'x-html-editor-tip'
44081         },
44082         strikethrough : {
44083             title: 'Strikethrough',
44084             text: 'Strikethrough the selected text.',
44085             cls: 'x-html-editor-tip'
44086         },
44087         increasefontsize : {
44088             title: 'Grow Text',
44089             text: 'Increase the font size.',
44090             cls: 'x-html-editor-tip'
44091         },
44092         decreasefontsize : {
44093             title: 'Shrink Text',
44094             text: 'Decrease the font size.',
44095             cls: 'x-html-editor-tip'
44096         },
44097         backcolor : {
44098             title: 'Text Highlight Color',
44099             text: 'Change the background color of the selected text.',
44100             cls: 'x-html-editor-tip'
44101         },
44102         forecolor : {
44103             title: 'Font Color',
44104             text: 'Change the color of the selected text.',
44105             cls: 'x-html-editor-tip'
44106         },
44107         justifyleft : {
44108             title: 'Align Text Left',
44109             text: 'Align text to the left.',
44110             cls: 'x-html-editor-tip'
44111         },
44112         justifycenter : {
44113             title: 'Center Text',
44114             text: 'Center text in the editor.',
44115             cls: 'x-html-editor-tip'
44116         },
44117         justifyright : {
44118             title: 'Align Text Right',
44119             text: 'Align text to the right.',
44120             cls: 'x-html-editor-tip'
44121         },
44122         insertunorderedlist : {
44123             title: 'Bullet List',
44124             text: 'Start a bulleted list.',
44125             cls: 'x-html-editor-tip'
44126         },
44127         insertorderedlist : {
44128             title: 'Numbered List',
44129             text: 'Start a numbered list.',
44130             cls: 'x-html-editor-tip'
44131         },
44132         createlink : {
44133             title: 'Hyperlink',
44134             text: 'Make the selected text a hyperlink.',
44135             cls: 'x-html-editor-tip'
44136         },
44137         sourceedit : {
44138             title: 'Source Edit',
44139             text: 'Switch to source editing mode.',
44140             cls: 'x-html-editor-tip'
44141         }
44142     },
44143     // private
44144     onDestroy : function(){
44145         if(this.rendered){
44146             
44147             this.tb.items.each(function(item){
44148                 if(item.menu){
44149                     item.menu.removeAll();
44150                     if(item.menu.el){
44151                         item.menu.el.destroy();
44152                     }
44153                 }
44154                 item.destroy();
44155             });
44156              
44157         }
44158     },
44159     onFirstFocus: function() {
44160         this.tb.items.each(function(item){
44161            item.enable();
44162         });
44163     }
44164 });
44165
44166
44167
44168
44169 // <script type="text/javascript">
44170 /*
44171  * Based on
44172  * Ext JS Library 1.1.1
44173  * Copyright(c) 2006-2007, Ext JS, LLC.
44174  *  
44175  
44176  */
44177
44178  
44179 /**
44180  * @class Roo.form.HtmlEditor.ToolbarContext
44181  * Context Toolbar
44182  * 
44183  * Usage:
44184  *
44185  new Roo.form.HtmlEditor({
44186     ....
44187     toolbars : [
44188         { xtype: 'ToolbarStandard', styles : {} }
44189         { xtype: 'ToolbarContext', disable : {} }
44190     ]
44191 })
44192
44193      
44194  * 
44195  * @config : {Object} disable List of elements to disable.. (not done yet.)
44196  * @config : {Object} styles  Map of styles available.
44197  * 
44198  */
44199
44200 Roo.form.HtmlEditor.ToolbarContext = function(config)
44201 {
44202     
44203     Roo.apply(this, config);
44204     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44205     // dont call parent... till later.
44206     this.styles = this.styles || {};
44207 }
44208
44209  
44210
44211 Roo.form.HtmlEditor.ToolbarContext.types = {
44212     'IMG' : {
44213         width : {
44214             title: "Width",
44215             width: 40
44216         },
44217         height:  {
44218             title: "Height",
44219             width: 40
44220         },
44221         align: {
44222             title: "Align",
44223             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44224             width : 80
44225             
44226         },
44227         border: {
44228             title: "Border",
44229             width: 40
44230         },
44231         alt: {
44232             title: "Alt",
44233             width: 120
44234         },
44235         src : {
44236             title: "Src",
44237             width: 220
44238         }
44239         
44240     },
44241     'A' : {
44242         name : {
44243             title: "Name",
44244             width: 50
44245         },
44246         target:  {
44247             title: "Target",
44248             width: 120
44249         },
44250         href:  {
44251             title: "Href",
44252             width: 220
44253         } // border?
44254         
44255     },
44256     'TABLE' : {
44257         rows : {
44258             title: "Rows",
44259             width: 20
44260         },
44261         cols : {
44262             title: "Cols",
44263             width: 20
44264         },
44265         width : {
44266             title: "Width",
44267             width: 40
44268         },
44269         height : {
44270             title: "Height",
44271             width: 40
44272         },
44273         border : {
44274             title: "Border",
44275             width: 20
44276         }
44277     },
44278     'TD' : {
44279         width : {
44280             title: "Width",
44281             width: 40
44282         },
44283         height : {
44284             title: "Height",
44285             width: 40
44286         },   
44287         align: {
44288             title: "Align",
44289             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44290             width: 80
44291         },
44292         valign: {
44293             title: "Valign",
44294             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44295             width: 80
44296         },
44297         colspan: {
44298             title: "Colspan",
44299             width: 20
44300             
44301         },
44302          'font-family'  : {
44303             title : "Font",
44304             style : 'fontFamily',
44305             displayField: 'display',
44306             optname : 'font-family',
44307             width: 140
44308         }
44309     },
44310     'INPUT' : {
44311         name : {
44312             title: "name",
44313             width: 120
44314         },
44315         value : {
44316             title: "Value",
44317             width: 120
44318         },
44319         width : {
44320             title: "Width",
44321             width: 40
44322         }
44323     },
44324     'LABEL' : {
44325         'for' : {
44326             title: "For",
44327             width: 120
44328         }
44329     },
44330     'TEXTAREA' : {
44331           name : {
44332             title: "name",
44333             width: 120
44334         },
44335         rows : {
44336             title: "Rows",
44337             width: 20
44338         },
44339         cols : {
44340             title: "Cols",
44341             width: 20
44342         }
44343     },
44344     'SELECT' : {
44345         name : {
44346             title: "name",
44347             width: 120
44348         },
44349         selectoptions : {
44350             title: "Options",
44351             width: 200
44352         }
44353     },
44354     
44355     // should we really allow this??
44356     // should this just be 
44357     'BODY' : {
44358         title : {
44359             title: "Title",
44360             width: 200,
44361             disabled : true
44362         }
44363     },
44364     'SPAN' : {
44365         'font-family'  : {
44366             title : "Font",
44367             style : 'fontFamily',
44368             displayField: 'display',
44369             optname : 'font-family',
44370             width: 140
44371         }
44372     },
44373     'DIV' : {
44374         'font-family'  : {
44375             title : "Font",
44376             style : 'fontFamily',
44377             displayField: 'display',
44378             optname : 'font-family',
44379             width: 140
44380         }
44381     },
44382      'P' : {
44383         'font-family'  : {
44384             title : "Font",
44385             style : 'fontFamily',
44386             displayField: 'display',
44387             optname : 'font-family',
44388             width: 140
44389         }
44390     },
44391     
44392     '*' : {
44393         // empty..
44394     }
44395
44396 };
44397
44398 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44399 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44400
44401 Roo.form.HtmlEditor.ToolbarContext.options = {
44402         'font-family'  : [ 
44403                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44404                 [ 'Courier New', 'Courier New'],
44405                 [ 'Tahoma', 'Tahoma'],
44406                 [ 'Times New Roman,serif', 'Times'],
44407                 [ 'Verdana','Verdana' ]
44408         ]
44409 };
44410
44411 // fixme - these need to be configurable..
44412  
44413
44414 //Roo.form.HtmlEditor.ToolbarContext.types
44415
44416
44417 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44418     
44419     tb: false,
44420     
44421     rendered: false,
44422     
44423     editor : false,
44424     editorcore : false,
44425     /**
44426      * @cfg {Object} disable  List of toolbar elements to disable
44427          
44428      */
44429     disable : false,
44430     /**
44431      * @cfg {Object} styles List of styles 
44432      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44433      *
44434      * These must be defined in the page, so they get rendered correctly..
44435      * .headline { }
44436      * TD.underline { }
44437      * 
44438      */
44439     styles : false,
44440     
44441     options: false,
44442     
44443     toolbars : false,
44444     
44445     init : function(editor)
44446     {
44447         this.editor = editor;
44448         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44449         var editorcore = this.editorcore;
44450         
44451         var fid = editorcore.frameId;
44452         var etb = this;
44453         function btn(id, toggle, handler){
44454             var xid = fid + '-'+ id ;
44455             return {
44456                 id : xid,
44457                 cmd : id,
44458                 cls : 'x-btn-icon x-edit-'+id,
44459                 enableToggle:toggle !== false,
44460                 scope: editorcore, // was editor...
44461                 handler:handler||editorcore.relayBtnCmd,
44462                 clickEvent:'mousedown',
44463                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44464                 tabIndex:-1
44465             };
44466         }
44467         // create a new element.
44468         var wdiv = editor.wrap.createChild({
44469                 tag: 'div'
44470             }, editor.wrap.dom.firstChild.nextSibling, true);
44471         
44472         // can we do this more than once??
44473         
44474          // stop form submits
44475       
44476  
44477         // disable everything...
44478         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44479         this.toolbars = {};
44480            
44481         for (var i in  ty) {
44482           
44483             this.toolbars[i] = this.buildToolbar(ty[i],i);
44484         }
44485         this.tb = this.toolbars.BODY;
44486         this.tb.el.show();
44487         this.buildFooter();
44488         this.footer.show();
44489         editor.on('hide', function( ) { this.footer.hide() }, this);
44490         editor.on('show', function( ) { this.footer.show() }, this);
44491         
44492          
44493         this.rendered = true;
44494         
44495         // the all the btns;
44496         editor.on('editorevent', this.updateToolbar, this);
44497         // other toolbars need to implement this..
44498         //editor.on('editmodechange', this.updateToolbar, this);
44499     },
44500     
44501     
44502     
44503     /**
44504      * Protected method that will not generally be called directly. It triggers
44505      * a toolbar update by reading the markup state of the current selection in the editor.
44506      *
44507      * Note you can force an update by calling on('editorevent', scope, false)
44508      */
44509     updateToolbar: function(editor,ev,sel){
44510
44511         //Roo.log(ev);
44512         // capture mouse up - this is handy for selecting images..
44513         // perhaps should go somewhere else...
44514         if(!this.editorcore.activated){
44515              this.editor.onFirstFocus();
44516             return;
44517         }
44518         
44519         
44520         
44521         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44522         // selectNode - might want to handle IE?
44523         if (ev &&
44524             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44525             ev.target && ev.target.tagName == 'IMG') {
44526             // they have click on an image...
44527             // let's see if we can change the selection...
44528             sel = ev.target;
44529          
44530               var nodeRange = sel.ownerDocument.createRange();
44531             try {
44532                 nodeRange.selectNode(sel);
44533             } catch (e) {
44534                 nodeRange.selectNodeContents(sel);
44535             }
44536             //nodeRange.collapse(true);
44537             var s = this.editorcore.win.getSelection();
44538             s.removeAllRanges();
44539             s.addRange(nodeRange);
44540         }  
44541         
44542       
44543         var updateFooter = sel ? false : true;
44544         
44545         
44546         var ans = this.editorcore.getAllAncestors();
44547         
44548         // pick
44549         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44550         
44551         if (!sel) { 
44552             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44553             sel = sel ? sel : this.editorcore.doc.body;
44554             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44555             
44556         }
44557         // pick a menu that exists..
44558         var tn = sel.tagName.toUpperCase();
44559         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44560         
44561         tn = sel.tagName.toUpperCase();
44562         
44563         var lastSel = this.tb.selectedNode;
44564         
44565         this.tb.selectedNode = sel;
44566         
44567         // if current menu does not match..
44568         
44569         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44570                 
44571             this.tb.el.hide();
44572             ///console.log("show: " + tn);
44573             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44574             this.tb.el.show();
44575             // update name
44576             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44577             
44578             
44579             // update attributes
44580             if (this.tb.fields) {
44581                 this.tb.fields.each(function(e) {
44582                     if (e.stylename) {
44583                         e.setValue(sel.style[e.stylename]);
44584                         return;
44585                     } 
44586                    e.setValue(sel.getAttribute(e.attrname));
44587                 });
44588             }
44589             
44590             var hasStyles = false;
44591             for(var i in this.styles) {
44592                 hasStyles = true;
44593                 break;
44594             }
44595             
44596             // update styles
44597             if (hasStyles) { 
44598                 var st = this.tb.fields.item(0);
44599                 
44600                 st.store.removeAll();
44601                
44602                 
44603                 var cn = sel.className.split(/\s+/);
44604                 
44605                 var avs = [];
44606                 if (this.styles['*']) {
44607                     
44608                     Roo.each(this.styles['*'], function(v) {
44609                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44610                     });
44611                 }
44612                 if (this.styles[tn]) { 
44613                     Roo.each(this.styles[tn], function(v) {
44614                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44615                     });
44616                 }
44617                 
44618                 st.store.loadData(avs);
44619                 st.collapse();
44620                 st.setValue(cn);
44621             }
44622             // flag our selected Node.
44623             this.tb.selectedNode = sel;
44624            
44625            
44626             Roo.menu.MenuMgr.hideAll();
44627
44628         }
44629         
44630         if (!updateFooter) {
44631             //this.footDisp.dom.innerHTML = ''; 
44632             return;
44633         }
44634         // update the footer
44635         //
44636         var html = '';
44637         
44638         this.footerEls = ans.reverse();
44639         Roo.each(this.footerEls, function(a,i) {
44640             if (!a) { return; }
44641             html += html.length ? ' &gt; '  :  '';
44642             
44643             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44644             
44645         });
44646        
44647         // 
44648         var sz = this.footDisp.up('td').getSize();
44649         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44650         this.footDisp.dom.style.marginLeft = '5px';
44651         
44652         this.footDisp.dom.style.overflow = 'hidden';
44653         
44654         this.footDisp.dom.innerHTML = html;
44655             
44656         //this.editorsyncValue();
44657     },
44658      
44659     
44660    
44661        
44662     // private
44663     onDestroy : function(){
44664         if(this.rendered){
44665             
44666             this.tb.items.each(function(item){
44667                 if(item.menu){
44668                     item.menu.removeAll();
44669                     if(item.menu.el){
44670                         item.menu.el.destroy();
44671                     }
44672                 }
44673                 item.destroy();
44674             });
44675              
44676         }
44677     },
44678     onFirstFocus: function() {
44679         // need to do this for all the toolbars..
44680         this.tb.items.each(function(item){
44681            item.enable();
44682         });
44683     },
44684     buildToolbar: function(tlist, nm)
44685     {
44686         var editor = this.editor;
44687         var editorcore = this.editorcore;
44688          // create a new element.
44689         var wdiv = editor.wrap.createChild({
44690                 tag: 'div'
44691             }, editor.wrap.dom.firstChild.nextSibling, true);
44692         
44693        
44694         var tb = new Roo.Toolbar(wdiv);
44695         // add the name..
44696         
44697         tb.add(nm+ ":&nbsp;");
44698         
44699         var styles = [];
44700         for(var i in this.styles) {
44701             styles.push(i);
44702         }
44703         
44704         // styles...
44705         if (styles && styles.length) {
44706             
44707             // this needs a multi-select checkbox...
44708             tb.addField( new Roo.form.ComboBox({
44709                 store: new Roo.data.SimpleStore({
44710                     id : 'val',
44711                     fields: ['val', 'selected'],
44712                     data : [] 
44713                 }),
44714                 name : '-roo-edit-className',
44715                 attrname : 'className',
44716                 displayField: 'val',
44717                 typeAhead: false,
44718                 mode: 'local',
44719                 editable : false,
44720                 triggerAction: 'all',
44721                 emptyText:'Select Style',
44722                 selectOnFocus:true,
44723                 width: 130,
44724                 listeners : {
44725                     'select': function(c, r, i) {
44726                         // initial support only for on class per el..
44727                         tb.selectedNode.className =  r ? r.get('val') : '';
44728                         editorcore.syncValue();
44729                     }
44730                 }
44731     
44732             }));
44733         }
44734         
44735         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44736         var tbops = tbc.options;
44737         
44738         for (var i in tlist) {
44739             
44740             var item = tlist[i];
44741             tb.add(item.title + ":&nbsp;");
44742             
44743             
44744             //optname == used so you can configure the options available..
44745             var opts = item.opts ? item.opts : false;
44746             if (item.optname) {
44747                 opts = tbops[item.optname];
44748            
44749             }
44750             
44751             if (opts) {
44752                 // opts == pulldown..
44753                 tb.addField( new Roo.form.ComboBox({
44754                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44755                         id : 'val',
44756                         fields: ['val', 'display'],
44757                         data : opts  
44758                     }),
44759                     name : '-roo-edit-' + i,
44760                     attrname : i,
44761                     stylename : item.style ? item.style : false,
44762                     displayField: item.displayField ? item.displayField : 'val',
44763                     valueField :  'val',
44764                     typeAhead: false,
44765                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44766                     editable : false,
44767                     triggerAction: 'all',
44768                     emptyText:'Select',
44769                     selectOnFocus:true,
44770                     width: item.width ? item.width  : 130,
44771                     listeners : {
44772                         'select': function(c, r, i) {
44773                             if (c.stylename) {
44774                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44775                                 return;
44776                             }
44777                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44778                         }
44779                     }
44780
44781                 }));
44782                 continue;
44783                     
44784                  
44785                 
44786                 tb.addField( new Roo.form.TextField({
44787                     name: i,
44788                     width: 100,
44789                     //allowBlank:false,
44790                     value: ''
44791                 }));
44792                 continue;
44793             }
44794             tb.addField( new Roo.form.TextField({
44795                 name: '-roo-edit-' + i,
44796                 attrname : i,
44797                 
44798                 width: item.width,
44799                 //allowBlank:true,
44800                 value: '',
44801                 listeners: {
44802                     'change' : function(f, nv, ov) {
44803                         tb.selectedNode.setAttribute(f.attrname, nv);
44804                     }
44805                 }
44806             }));
44807              
44808         }
44809         
44810         var _this = this;
44811         
44812         if(nm == 'BODY'){
44813             tb.addSeparator();
44814         
44815             tb.addButton( {
44816                 text: 'Stylesheets',
44817
44818                 listeners : {
44819                     click : function ()
44820                     {
44821                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44822                     }
44823                 }
44824             });
44825         }
44826         
44827         tb.addFill();
44828         tb.addButton( {
44829             text: 'Remove Tag',
44830     
44831             listeners : {
44832                 click : function ()
44833                 {
44834                     // remove
44835                     // undo does not work.
44836                      
44837                     var sn = tb.selectedNode;
44838                     
44839                     var pn = sn.parentNode;
44840                     
44841                     var stn =  sn.childNodes[0];
44842                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44843                     while (sn.childNodes.length) {
44844                         var node = sn.childNodes[0];
44845                         sn.removeChild(node);
44846                         //Roo.log(node);
44847                         pn.insertBefore(node, sn);
44848                         
44849                     }
44850                     pn.removeChild(sn);
44851                     var range = editorcore.createRange();
44852         
44853                     range.setStart(stn,0);
44854                     range.setEnd(en,0); //????
44855                     //range.selectNode(sel);
44856                     
44857                     
44858                     var selection = editorcore.getSelection();
44859                     selection.removeAllRanges();
44860                     selection.addRange(range);
44861                     
44862                     
44863                     
44864                     //_this.updateToolbar(null, null, pn);
44865                     _this.updateToolbar(null, null, null);
44866                     _this.footDisp.dom.innerHTML = ''; 
44867                 }
44868             }
44869             
44870                     
44871                 
44872             
44873         });
44874         
44875         
44876         tb.el.on('click', function(e){
44877             e.preventDefault(); // what does this do?
44878         });
44879         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44880         tb.el.hide();
44881         tb.name = nm;
44882         // dont need to disable them... as they will get hidden
44883         return tb;
44884          
44885         
44886     },
44887     buildFooter : function()
44888     {
44889         
44890         var fel = this.editor.wrap.createChild();
44891         this.footer = new Roo.Toolbar(fel);
44892         // toolbar has scrolly on left / right?
44893         var footDisp= new Roo.Toolbar.Fill();
44894         var _t = this;
44895         this.footer.add(
44896             {
44897                 text : '&lt;',
44898                 xtype: 'Button',
44899                 handler : function() {
44900                     _t.footDisp.scrollTo('left',0,true)
44901                 }
44902             }
44903         );
44904         this.footer.add( footDisp );
44905         this.footer.add( 
44906             {
44907                 text : '&gt;',
44908                 xtype: 'Button',
44909                 handler : function() {
44910                     // no animation..
44911                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44912                 }
44913             }
44914         );
44915         var fel = Roo.get(footDisp.el);
44916         fel.addClass('x-editor-context');
44917         this.footDispWrap = fel; 
44918         this.footDispWrap.overflow  = 'hidden';
44919         
44920         this.footDisp = fel.createChild();
44921         this.footDispWrap.on('click', this.onContextClick, this)
44922         
44923         
44924     },
44925     onContextClick : function (ev,dom)
44926     {
44927         ev.preventDefault();
44928         var  cn = dom.className;
44929         //Roo.log(cn);
44930         if (!cn.match(/x-ed-loc-/)) {
44931             return;
44932         }
44933         var n = cn.split('-').pop();
44934         var ans = this.footerEls;
44935         var sel = ans[n];
44936         
44937          // pick
44938         var range = this.editorcore.createRange();
44939         
44940         range.selectNodeContents(sel);
44941         //range.selectNode(sel);
44942         
44943         
44944         var selection = this.editorcore.getSelection();
44945         selection.removeAllRanges();
44946         selection.addRange(range);
44947         
44948         
44949         
44950         this.updateToolbar(null, null, sel);
44951         
44952         
44953     }
44954     
44955     
44956     
44957     
44958     
44959 });
44960
44961
44962
44963
44964
44965 /*
44966  * Based on:
44967  * Ext JS Library 1.1.1
44968  * Copyright(c) 2006-2007, Ext JS, LLC.
44969  *
44970  * Originally Released Under LGPL - original licence link has changed is not relivant.
44971  *
44972  * Fork - LGPL
44973  * <script type="text/javascript">
44974  */
44975  
44976 /**
44977  * @class Roo.form.BasicForm
44978  * @extends Roo.util.Observable
44979  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44980  * @constructor
44981  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44982  * @param {Object} config Configuration options
44983  */
44984 Roo.form.BasicForm = function(el, config){
44985     this.allItems = [];
44986     this.childForms = [];
44987     Roo.apply(this, config);
44988     /*
44989      * The Roo.form.Field items in this form.
44990      * @type MixedCollection
44991      */
44992      
44993      
44994     this.items = new Roo.util.MixedCollection(false, function(o){
44995         return o.id || (o.id = Roo.id());
44996     });
44997     this.addEvents({
44998         /**
44999          * @event beforeaction
45000          * Fires before any action is performed. Return false to cancel the action.
45001          * @param {Form} this
45002          * @param {Action} action The action to be performed
45003          */
45004         beforeaction: true,
45005         /**
45006          * @event actionfailed
45007          * Fires when an action fails.
45008          * @param {Form} this
45009          * @param {Action} action The action that failed
45010          */
45011         actionfailed : true,
45012         /**
45013          * @event actioncomplete
45014          * Fires when an action is completed.
45015          * @param {Form} this
45016          * @param {Action} action The action that completed
45017          */
45018         actioncomplete : true
45019     });
45020     if(el){
45021         this.initEl(el);
45022     }
45023     Roo.form.BasicForm.superclass.constructor.call(this);
45024 };
45025
45026 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45027     /**
45028      * @cfg {String} method
45029      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45030      */
45031     /**
45032      * @cfg {DataReader} reader
45033      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45034      * This is optional as there is built-in support for processing JSON.
45035      */
45036     /**
45037      * @cfg {DataReader} errorReader
45038      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45039      * This is completely optional as there is built-in support for processing JSON.
45040      */
45041     /**
45042      * @cfg {String} url
45043      * The URL to use for form actions if one isn't supplied in the action options.
45044      */
45045     /**
45046      * @cfg {Boolean} fileUpload
45047      * Set to true if this form is a file upload.
45048      */
45049      
45050     /**
45051      * @cfg {Object} baseParams
45052      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45053      */
45054      /**
45055      
45056     /**
45057      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45058      */
45059     timeout: 30,
45060
45061     // private
45062     activeAction : null,
45063
45064     /**
45065      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45066      * or setValues() data instead of when the form was first created.
45067      */
45068     trackResetOnLoad : false,
45069     
45070     
45071     /**
45072      * childForms - used for multi-tab forms
45073      * @type {Array}
45074      */
45075     childForms : false,
45076     
45077     /**
45078      * allItems - full list of fields.
45079      * @type {Array}
45080      */
45081     allItems : false,
45082     
45083     /**
45084      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45085      * element by passing it or its id or mask the form itself by passing in true.
45086      * @type Mixed
45087      */
45088     waitMsgTarget : false,
45089
45090     // private
45091     initEl : function(el){
45092         this.el = Roo.get(el);
45093         this.id = this.el.id || Roo.id();
45094         this.el.on('submit', this.onSubmit, this);
45095         this.el.addClass('x-form');
45096     },
45097
45098     // private
45099     onSubmit : function(e){
45100         e.stopEvent();
45101     },
45102
45103     /**
45104      * Returns true if client-side validation on the form is successful.
45105      * @return Boolean
45106      */
45107     isValid : function(){
45108         var valid = true;
45109         this.items.each(function(f){
45110            if(!f.validate()){
45111                valid = false;
45112            }
45113         });
45114         return valid;
45115     },
45116
45117     /**
45118      * Returns true if any fields in this form have changed since their original load.
45119      * @return Boolean
45120      */
45121     isDirty : function(){
45122         var dirty = false;
45123         this.items.each(function(f){
45124            if(f.isDirty()){
45125                dirty = true;
45126                return false;
45127            }
45128         });
45129         return dirty;
45130     },
45131
45132     /**
45133      * Performs a predefined action (submit or load) or custom actions you define on this form.
45134      * @param {String} actionName The name of the action type
45135      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45136      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45137      * accept other config options):
45138      * <pre>
45139 Property          Type             Description
45140 ----------------  ---------------  ----------------------------------------------------------------------------------
45141 url               String           The url for the action (defaults to the form's url)
45142 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45143 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45144 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45145                                    validate the form on the client (defaults to false)
45146      * </pre>
45147      * @return {BasicForm} this
45148      */
45149     doAction : function(action, options){
45150         if(typeof action == 'string'){
45151             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45152         }
45153         if(this.fireEvent('beforeaction', this, action) !== false){
45154             this.beforeAction(action);
45155             action.run.defer(100, action);
45156         }
45157         return this;
45158     },
45159
45160     /**
45161      * Shortcut to do a submit action.
45162      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45163      * @return {BasicForm} this
45164      */
45165     submit : function(options){
45166         this.doAction('submit', options);
45167         return this;
45168     },
45169
45170     /**
45171      * Shortcut to do a load action.
45172      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45173      * @return {BasicForm} this
45174      */
45175     load : function(options){
45176         this.doAction('load', options);
45177         return this;
45178     },
45179
45180     /**
45181      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45182      * @param {Record} record The record to edit
45183      * @return {BasicForm} this
45184      */
45185     updateRecord : function(record){
45186         record.beginEdit();
45187         var fs = record.fields;
45188         fs.each(function(f){
45189             var field = this.findField(f.name);
45190             if(field){
45191                 record.set(f.name, field.getValue());
45192             }
45193         }, this);
45194         record.endEdit();
45195         return this;
45196     },
45197
45198     /**
45199      * Loads an Roo.data.Record into this form.
45200      * @param {Record} record The record to load
45201      * @return {BasicForm} this
45202      */
45203     loadRecord : function(record){
45204         this.setValues(record.data);
45205         return this;
45206     },
45207
45208     // private
45209     beforeAction : function(action){
45210         var o = action.options;
45211         
45212        
45213         if(this.waitMsgTarget === true){
45214             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45215         }else if(this.waitMsgTarget){
45216             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45217             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45218         }else {
45219             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45220         }
45221          
45222     },
45223
45224     // private
45225     afterAction : function(action, success){
45226         this.activeAction = null;
45227         var o = action.options;
45228         
45229         if(this.waitMsgTarget === true){
45230             this.el.unmask();
45231         }else if(this.waitMsgTarget){
45232             this.waitMsgTarget.unmask();
45233         }else{
45234             Roo.MessageBox.updateProgress(1);
45235             Roo.MessageBox.hide();
45236         }
45237          
45238         if(success){
45239             if(o.reset){
45240                 this.reset();
45241             }
45242             Roo.callback(o.success, o.scope, [this, action]);
45243             this.fireEvent('actioncomplete', this, action);
45244             
45245         }else{
45246             
45247             // failure condition..
45248             // we have a scenario where updates need confirming.
45249             // eg. if a locking scenario exists..
45250             // we look for { errors : { needs_confirm : true }} in the response.
45251             if (
45252                 (typeof(action.result) != 'undefined')  &&
45253                 (typeof(action.result.errors) != 'undefined')  &&
45254                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45255            ){
45256                 var _t = this;
45257                 Roo.MessageBox.confirm(
45258                     "Change requires confirmation",
45259                     action.result.errorMsg,
45260                     function(r) {
45261                         if (r != 'yes') {
45262                             return;
45263                         }
45264                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45265                     }
45266                     
45267                 );
45268                 
45269                 
45270                 
45271                 return;
45272             }
45273             
45274             Roo.callback(o.failure, o.scope, [this, action]);
45275             // show an error message if no failed handler is set..
45276             if (!this.hasListener('actionfailed')) {
45277                 Roo.MessageBox.alert("Error",
45278                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45279                         action.result.errorMsg :
45280                         "Saving Failed, please check your entries or try again"
45281                 );
45282             }
45283             
45284             this.fireEvent('actionfailed', this, action);
45285         }
45286         
45287     },
45288
45289     /**
45290      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45291      * @param {String} id The value to search for
45292      * @return Field
45293      */
45294     findField : function(id){
45295         var field = this.items.get(id);
45296         if(!field){
45297             this.items.each(function(f){
45298                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45299                     field = f;
45300                     return false;
45301                 }
45302             });
45303         }
45304         return field || null;
45305     },
45306
45307     /**
45308      * Add a secondary form to this one, 
45309      * Used to provide tabbed forms. One form is primary, with hidden values 
45310      * which mirror the elements from the other forms.
45311      * 
45312      * @param {Roo.form.Form} form to add.
45313      * 
45314      */
45315     addForm : function(form)
45316     {
45317        
45318         if (this.childForms.indexOf(form) > -1) {
45319             // already added..
45320             return;
45321         }
45322         this.childForms.push(form);
45323         var n = '';
45324         Roo.each(form.allItems, function (fe) {
45325             
45326             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45327             if (this.findField(n)) { // already added..
45328                 return;
45329             }
45330             var add = new Roo.form.Hidden({
45331                 name : n
45332             });
45333             add.render(this.el);
45334             
45335             this.add( add );
45336         }, this);
45337         
45338     },
45339     /**
45340      * Mark fields in this form invalid in bulk.
45341      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45342      * @return {BasicForm} this
45343      */
45344     markInvalid : function(errors){
45345         if(errors instanceof Array){
45346             for(var i = 0, len = errors.length; i < len; i++){
45347                 var fieldError = errors[i];
45348                 var f = this.findField(fieldError.id);
45349                 if(f){
45350                     f.markInvalid(fieldError.msg);
45351                 }
45352             }
45353         }else{
45354             var field, id;
45355             for(id in errors){
45356                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45357                     field.markInvalid(errors[id]);
45358                 }
45359             }
45360         }
45361         Roo.each(this.childForms || [], function (f) {
45362             f.markInvalid(errors);
45363         });
45364         
45365         return this;
45366     },
45367
45368     /**
45369      * Set values for fields in this form in bulk.
45370      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45371      * @return {BasicForm} this
45372      */
45373     setValues : function(values){
45374         if(values instanceof Array){ // array of objects
45375             for(var i = 0, len = values.length; i < len; i++){
45376                 var v = values[i];
45377                 var f = this.findField(v.id);
45378                 if(f){
45379                     f.setValue(v.value);
45380                     if(this.trackResetOnLoad){
45381                         f.originalValue = f.getValue();
45382                     }
45383                 }
45384             }
45385         }else{ // object hash
45386             var field, id;
45387             for(id in values){
45388                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45389                     
45390                     if (field.setFromData && 
45391                         field.valueField && 
45392                         field.displayField &&
45393                         // combos' with local stores can 
45394                         // be queried via setValue()
45395                         // to set their value..
45396                         (field.store && !field.store.isLocal)
45397                         ) {
45398                         // it's a combo
45399                         var sd = { };
45400                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45401                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45402                         field.setFromData(sd);
45403                         
45404                     } else {
45405                         field.setValue(values[id]);
45406                     }
45407                     
45408                     
45409                     if(this.trackResetOnLoad){
45410                         field.originalValue = field.getValue();
45411                     }
45412                 }
45413             }
45414         }
45415          
45416         Roo.each(this.childForms || [], function (f) {
45417             f.setValues(values);
45418         });
45419                 
45420         return this;
45421     },
45422
45423     /**
45424      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45425      * they are returned as an array.
45426      * @param {Boolean} asString
45427      * @return {Object}
45428      */
45429     getValues : function(asString){
45430         if (this.childForms) {
45431             // copy values from the child forms
45432             Roo.each(this.childForms, function (f) {
45433                 this.setValues(f.getValues());
45434             }, this);
45435         }
45436         
45437         
45438         
45439         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45440         if(asString === true){
45441             return fs;
45442         }
45443         return Roo.urlDecode(fs);
45444     },
45445     
45446     /**
45447      * Returns the fields in this form as an object with key/value pairs. 
45448      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45449      * @return {Object}
45450      */
45451     getFieldValues : function(with_hidden)
45452     {
45453         if (this.childForms) {
45454             // copy values from the child forms
45455             // should this call getFieldValues - probably not as we do not currently copy
45456             // hidden fields when we generate..
45457             Roo.each(this.childForms, function (f) {
45458                 this.setValues(f.getValues());
45459             }, this);
45460         }
45461         
45462         var ret = {};
45463         this.items.each(function(f){
45464             if (!f.getName()) {
45465                 return;
45466             }
45467             var v = f.getValue();
45468             if (f.inputType =='radio') {
45469                 if (typeof(ret[f.getName()]) == 'undefined') {
45470                     ret[f.getName()] = ''; // empty..
45471                 }
45472                 
45473                 if (!f.el.dom.checked) {
45474                     return;
45475                     
45476                 }
45477                 v = f.el.dom.value;
45478                 
45479             }
45480             
45481             // not sure if this supported any more..
45482             if ((typeof(v) == 'object') && f.getRawValue) {
45483                 v = f.getRawValue() ; // dates..
45484             }
45485             // combo boxes where name != hiddenName...
45486             if (f.name != f.getName()) {
45487                 ret[f.name] = f.getRawValue();
45488             }
45489             ret[f.getName()] = v;
45490         });
45491         
45492         return ret;
45493     },
45494
45495     /**
45496      * Clears all invalid messages in this form.
45497      * @return {BasicForm} this
45498      */
45499     clearInvalid : function(){
45500         this.items.each(function(f){
45501            f.clearInvalid();
45502         });
45503         
45504         Roo.each(this.childForms || [], function (f) {
45505             f.clearInvalid();
45506         });
45507         
45508         
45509         return this;
45510     },
45511
45512     /**
45513      * Resets this form.
45514      * @return {BasicForm} this
45515      */
45516     reset : function(){
45517         this.items.each(function(f){
45518             f.reset();
45519         });
45520         
45521         Roo.each(this.childForms || [], function (f) {
45522             f.reset();
45523         });
45524        
45525         
45526         return this;
45527     },
45528
45529     /**
45530      * Add Roo.form components to this form.
45531      * @param {Field} field1
45532      * @param {Field} field2 (optional)
45533      * @param {Field} etc (optional)
45534      * @return {BasicForm} this
45535      */
45536     add : function(){
45537         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45538         return this;
45539     },
45540
45541
45542     /**
45543      * Removes a field from the items collection (does NOT remove its markup).
45544      * @param {Field} field
45545      * @return {BasicForm} this
45546      */
45547     remove : function(field){
45548         this.items.remove(field);
45549         return this;
45550     },
45551
45552     /**
45553      * Looks at the fields in this form, checks them for an id attribute,
45554      * and calls applyTo on the existing dom element with that id.
45555      * @return {BasicForm} this
45556      */
45557     render : function(){
45558         this.items.each(function(f){
45559             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45560                 f.applyTo(f.id);
45561             }
45562         });
45563         return this;
45564     },
45565
45566     /**
45567      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45568      * @param {Object} values
45569      * @return {BasicForm} this
45570      */
45571     applyToFields : function(o){
45572         this.items.each(function(f){
45573            Roo.apply(f, o);
45574         });
45575         return this;
45576     },
45577
45578     /**
45579      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45580      * @param {Object} values
45581      * @return {BasicForm} this
45582      */
45583     applyIfToFields : function(o){
45584         this.items.each(function(f){
45585            Roo.applyIf(f, o);
45586         });
45587         return this;
45588     }
45589 });
45590
45591 // back compat
45592 Roo.BasicForm = Roo.form.BasicForm;/*
45593  * Based on:
45594  * Ext JS Library 1.1.1
45595  * Copyright(c) 2006-2007, Ext JS, LLC.
45596  *
45597  * Originally Released Under LGPL - original licence link has changed is not relivant.
45598  *
45599  * Fork - LGPL
45600  * <script type="text/javascript">
45601  */
45602
45603 /**
45604  * @class Roo.form.Form
45605  * @extends Roo.form.BasicForm
45606  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45607  * @constructor
45608  * @param {Object} config Configuration options
45609  */
45610 Roo.form.Form = function(config){
45611     var xitems =  [];
45612     if (config.items) {
45613         xitems = config.items;
45614         delete config.items;
45615     }
45616    
45617     
45618     Roo.form.Form.superclass.constructor.call(this, null, config);
45619     this.url = this.url || this.action;
45620     if(!this.root){
45621         this.root = new Roo.form.Layout(Roo.applyIf({
45622             id: Roo.id()
45623         }, config));
45624     }
45625     this.active = this.root;
45626     /**
45627      * Array of all the buttons that have been added to this form via {@link addButton}
45628      * @type Array
45629      */
45630     this.buttons = [];
45631     this.allItems = [];
45632     this.addEvents({
45633         /**
45634          * @event clientvalidation
45635          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45636          * @param {Form} this
45637          * @param {Boolean} valid true if the form has passed client-side validation
45638          */
45639         clientvalidation: true,
45640         /**
45641          * @event rendered
45642          * Fires when the form is rendered
45643          * @param {Roo.form.Form} form
45644          */
45645         rendered : true
45646     });
45647     
45648     if (this.progressUrl) {
45649             // push a hidden field onto the list of fields..
45650             this.addxtype( {
45651                     xns: Roo.form, 
45652                     xtype : 'Hidden', 
45653                     name : 'UPLOAD_IDENTIFIER' 
45654             });
45655         }
45656         
45657     
45658     Roo.each(xitems, this.addxtype, this);
45659     
45660     
45661     
45662 };
45663
45664 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45665     /**
45666      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45667      */
45668     /**
45669      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45670      */
45671     /**
45672      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45673      */
45674     buttonAlign:'center',
45675
45676     /**
45677      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45678      */
45679     minButtonWidth:75,
45680
45681     /**
45682      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45683      * This property cascades to child containers if not set.
45684      */
45685     labelAlign:'left',
45686
45687     /**
45688      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45689      * fires a looping event with that state. This is required to bind buttons to the valid
45690      * state using the config value formBind:true on the button.
45691      */
45692     monitorValid : false,
45693
45694     /**
45695      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45696      */
45697     monitorPoll : 200,
45698     
45699     /**
45700      * @cfg {String} progressUrl - Url to return progress data 
45701      */
45702     
45703     progressUrl : false,
45704   
45705     /**
45706      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45707      * fields are added and the column is closed. If no fields are passed the column remains open
45708      * until end() is called.
45709      * @param {Object} config The config to pass to the column
45710      * @param {Field} field1 (optional)
45711      * @param {Field} field2 (optional)
45712      * @param {Field} etc (optional)
45713      * @return Column The column container object
45714      */
45715     column : function(c){
45716         var col = new Roo.form.Column(c);
45717         this.start(col);
45718         if(arguments.length > 1){ // duplicate code required because of Opera
45719             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45720             this.end();
45721         }
45722         return col;
45723     },
45724
45725     /**
45726      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45727      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45728      * until end() is called.
45729      * @param {Object} config The config to pass to the fieldset
45730      * @param {Field} field1 (optional)
45731      * @param {Field} field2 (optional)
45732      * @param {Field} etc (optional)
45733      * @return FieldSet The fieldset container object
45734      */
45735     fieldset : function(c){
45736         var fs = new Roo.form.FieldSet(c);
45737         this.start(fs);
45738         if(arguments.length > 1){ // duplicate code required because of Opera
45739             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45740             this.end();
45741         }
45742         return fs;
45743     },
45744
45745     /**
45746      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45747      * fields are added and the container is closed. If no fields are passed the container remains open
45748      * until end() is called.
45749      * @param {Object} config The config to pass to the Layout
45750      * @param {Field} field1 (optional)
45751      * @param {Field} field2 (optional)
45752      * @param {Field} etc (optional)
45753      * @return Layout The container object
45754      */
45755     container : function(c){
45756         var l = new Roo.form.Layout(c);
45757         this.start(l);
45758         if(arguments.length > 1){ // duplicate code required because of Opera
45759             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45760             this.end();
45761         }
45762         return l;
45763     },
45764
45765     /**
45766      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45767      * @param {Object} container A Roo.form.Layout or subclass of Layout
45768      * @return {Form} this
45769      */
45770     start : function(c){
45771         // cascade label info
45772         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45773         this.active.stack.push(c);
45774         c.ownerCt = this.active;
45775         this.active = c;
45776         return this;
45777     },
45778
45779     /**
45780      * Closes the current open container
45781      * @return {Form} this
45782      */
45783     end : function(){
45784         if(this.active == this.root){
45785             return this;
45786         }
45787         this.active = this.active.ownerCt;
45788         return this;
45789     },
45790
45791     /**
45792      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45793      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45794      * as the label of the field.
45795      * @param {Field} field1
45796      * @param {Field} field2 (optional)
45797      * @param {Field} etc. (optional)
45798      * @return {Form} this
45799      */
45800     add : function(){
45801         this.active.stack.push.apply(this.active.stack, arguments);
45802         this.allItems.push.apply(this.allItems,arguments);
45803         var r = [];
45804         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45805             if(a[i].isFormField){
45806                 r.push(a[i]);
45807             }
45808         }
45809         if(r.length > 0){
45810             Roo.form.Form.superclass.add.apply(this, r);
45811         }
45812         return this;
45813     },
45814     
45815
45816     
45817     
45818     
45819      /**
45820      * Find any element that has been added to a form, using it's ID or name
45821      * This can include framesets, columns etc. along with regular fields..
45822      * @param {String} id - id or name to find.
45823      
45824      * @return {Element} e - or false if nothing found.
45825      */
45826     findbyId : function(id)
45827     {
45828         var ret = false;
45829         if (!id) {
45830             return ret;
45831         }
45832         Roo.each(this.allItems, function(f){
45833             if (f.id == id || f.name == id ){
45834                 ret = f;
45835                 return false;
45836             }
45837         });
45838         return ret;
45839     },
45840
45841     
45842     
45843     /**
45844      * Render this form into the passed container. This should only be called once!
45845      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45846      * @return {Form} this
45847      */
45848     render : function(ct)
45849     {
45850         
45851         
45852         
45853         ct = Roo.get(ct);
45854         var o = this.autoCreate || {
45855             tag: 'form',
45856             method : this.method || 'POST',
45857             id : this.id || Roo.id()
45858         };
45859         this.initEl(ct.createChild(o));
45860
45861         this.root.render(this.el);
45862         
45863        
45864              
45865         this.items.each(function(f){
45866             f.render('x-form-el-'+f.id);
45867         });
45868
45869         if(this.buttons.length > 0){
45870             // tables are required to maintain order and for correct IE layout
45871             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45872                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45873                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45874             }}, null, true);
45875             var tr = tb.getElementsByTagName('tr')[0];
45876             for(var i = 0, len = this.buttons.length; i < len; i++) {
45877                 var b = this.buttons[i];
45878                 var td = document.createElement('td');
45879                 td.className = 'x-form-btn-td';
45880                 b.render(tr.appendChild(td));
45881             }
45882         }
45883         if(this.monitorValid){ // initialize after render
45884             this.startMonitoring();
45885         }
45886         this.fireEvent('rendered', this);
45887         return this;
45888     },
45889
45890     /**
45891      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45892      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45893      * object or a valid Roo.DomHelper element config
45894      * @param {Function} handler The function called when the button is clicked
45895      * @param {Object} scope (optional) The scope of the handler function
45896      * @return {Roo.Button}
45897      */
45898     addButton : function(config, handler, scope){
45899         var bc = {
45900             handler: handler,
45901             scope: scope,
45902             minWidth: this.minButtonWidth,
45903             hideParent:true
45904         };
45905         if(typeof config == "string"){
45906             bc.text = config;
45907         }else{
45908             Roo.apply(bc, config);
45909         }
45910         var btn = new Roo.Button(null, bc);
45911         this.buttons.push(btn);
45912         return btn;
45913     },
45914
45915      /**
45916      * Adds a series of form elements (using the xtype property as the factory method.
45917      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45918      * @param {Object} config 
45919      */
45920     
45921     addxtype : function()
45922     {
45923         var ar = Array.prototype.slice.call(arguments, 0);
45924         var ret = false;
45925         for(var i = 0; i < ar.length; i++) {
45926             if (!ar[i]) {
45927                 continue; // skip -- if this happends something invalid got sent, we 
45928                 // should ignore it, as basically that interface element will not show up
45929                 // and that should be pretty obvious!!
45930             }
45931             
45932             if (Roo.form[ar[i].xtype]) {
45933                 ar[i].form = this;
45934                 var fe = Roo.factory(ar[i], Roo.form);
45935                 if (!ret) {
45936                     ret = fe;
45937                 }
45938                 fe.form = this;
45939                 if (fe.store) {
45940                     fe.store.form = this;
45941                 }
45942                 if (fe.isLayout) {  
45943                          
45944                     this.start(fe);
45945                     this.allItems.push(fe);
45946                     if (fe.items && fe.addxtype) {
45947                         fe.addxtype.apply(fe, fe.items);
45948                         delete fe.items;
45949                     }
45950                      this.end();
45951                     continue;
45952                 }
45953                 
45954                 
45955                  
45956                 this.add(fe);
45957               //  console.log('adding ' + ar[i].xtype);
45958             }
45959             if (ar[i].xtype == 'Button') {  
45960                 //console.log('adding button');
45961                 //console.log(ar[i]);
45962                 this.addButton(ar[i]);
45963                 this.allItems.push(fe);
45964                 continue;
45965             }
45966             
45967             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45968                 alert('end is not supported on xtype any more, use items');
45969             //    this.end();
45970             //    //console.log('adding end');
45971             }
45972             
45973         }
45974         return ret;
45975     },
45976     
45977     /**
45978      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45979      * option "monitorValid"
45980      */
45981     startMonitoring : function(){
45982         if(!this.bound){
45983             this.bound = true;
45984             Roo.TaskMgr.start({
45985                 run : this.bindHandler,
45986                 interval : this.monitorPoll || 200,
45987                 scope: this
45988             });
45989         }
45990     },
45991
45992     /**
45993      * Stops monitoring of the valid state of this form
45994      */
45995     stopMonitoring : function(){
45996         this.bound = false;
45997     },
45998
45999     // private
46000     bindHandler : function(){
46001         if(!this.bound){
46002             return false; // stops binding
46003         }
46004         var valid = true;
46005         this.items.each(function(f){
46006             if(!f.isValid(true)){
46007                 valid = false;
46008                 return false;
46009             }
46010         });
46011         for(var i = 0, len = this.buttons.length; i < len; i++){
46012             var btn = this.buttons[i];
46013             if(btn.formBind === true && btn.disabled === valid){
46014                 btn.setDisabled(!valid);
46015             }
46016         }
46017         this.fireEvent('clientvalidation', this, valid);
46018     }
46019     
46020     
46021     
46022     
46023     
46024     
46025     
46026     
46027 });
46028
46029
46030 // back compat
46031 Roo.Form = Roo.form.Form;
46032 /*
46033  * Based on:
46034  * Ext JS Library 1.1.1
46035  * Copyright(c) 2006-2007, Ext JS, LLC.
46036  *
46037  * Originally Released Under LGPL - original licence link has changed is not relivant.
46038  *
46039  * Fork - LGPL
46040  * <script type="text/javascript">
46041  */
46042
46043 // as we use this in bootstrap.
46044 Roo.namespace('Roo.form');
46045  /**
46046  * @class Roo.form.Action
46047  * Internal Class used to handle form actions
46048  * @constructor
46049  * @param {Roo.form.BasicForm} el The form element or its id
46050  * @param {Object} config Configuration options
46051  */
46052
46053  
46054  
46055 // define the action interface
46056 Roo.form.Action = function(form, options){
46057     this.form = form;
46058     this.options = options || {};
46059 };
46060 /**
46061  * Client Validation Failed
46062  * @const 
46063  */
46064 Roo.form.Action.CLIENT_INVALID = 'client';
46065 /**
46066  * Server Validation Failed
46067  * @const 
46068  */
46069 Roo.form.Action.SERVER_INVALID = 'server';
46070  /**
46071  * Connect to Server Failed
46072  * @const 
46073  */
46074 Roo.form.Action.CONNECT_FAILURE = 'connect';
46075 /**
46076  * Reading Data from Server Failed
46077  * @const 
46078  */
46079 Roo.form.Action.LOAD_FAILURE = 'load';
46080
46081 Roo.form.Action.prototype = {
46082     type : 'default',
46083     failureType : undefined,
46084     response : undefined,
46085     result : undefined,
46086
46087     // interface method
46088     run : function(options){
46089
46090     },
46091
46092     // interface method
46093     success : function(response){
46094
46095     },
46096
46097     // interface method
46098     handleResponse : function(response){
46099
46100     },
46101
46102     // default connection failure
46103     failure : function(response){
46104         
46105         this.response = response;
46106         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46107         this.form.afterAction(this, false);
46108     },
46109
46110     processResponse : function(response){
46111         this.response = response;
46112         if(!response.responseText){
46113             return true;
46114         }
46115         this.result = this.handleResponse(response);
46116         return this.result;
46117     },
46118
46119     // utility functions used internally
46120     getUrl : function(appendParams){
46121         var url = this.options.url || this.form.url || this.form.el.dom.action;
46122         if(appendParams){
46123             var p = this.getParams();
46124             if(p){
46125                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46126             }
46127         }
46128         return url;
46129     },
46130
46131     getMethod : function(){
46132         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46133     },
46134
46135     getParams : function(){
46136         var bp = this.form.baseParams;
46137         var p = this.options.params;
46138         if(p){
46139             if(typeof p == "object"){
46140                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46141             }else if(typeof p == 'string' && bp){
46142                 p += '&' + Roo.urlEncode(bp);
46143             }
46144         }else if(bp){
46145             p = Roo.urlEncode(bp);
46146         }
46147         return p;
46148     },
46149
46150     createCallback : function(){
46151         return {
46152             success: this.success,
46153             failure: this.failure,
46154             scope: this,
46155             timeout: (this.form.timeout*1000),
46156             upload: this.form.fileUpload ? this.success : undefined
46157         };
46158     }
46159 };
46160
46161 Roo.form.Action.Submit = function(form, options){
46162     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46163 };
46164
46165 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46166     type : 'submit',
46167
46168     haveProgress : false,
46169     uploadComplete : false,
46170     
46171     // uploadProgress indicator.
46172     uploadProgress : function()
46173     {
46174         if (!this.form.progressUrl) {
46175             return;
46176         }
46177         
46178         if (!this.haveProgress) {
46179             Roo.MessageBox.progress("Uploading", "Uploading");
46180         }
46181         if (this.uploadComplete) {
46182            Roo.MessageBox.hide();
46183            return;
46184         }
46185         
46186         this.haveProgress = true;
46187    
46188         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46189         
46190         var c = new Roo.data.Connection();
46191         c.request({
46192             url : this.form.progressUrl,
46193             params: {
46194                 id : uid
46195             },
46196             method: 'GET',
46197             success : function(req){
46198                //console.log(data);
46199                 var rdata = false;
46200                 var edata;
46201                 try  {
46202                    rdata = Roo.decode(req.responseText)
46203                 } catch (e) {
46204                     Roo.log("Invalid data from server..");
46205                     Roo.log(edata);
46206                     return;
46207                 }
46208                 if (!rdata || !rdata.success) {
46209                     Roo.log(rdata);
46210                     Roo.MessageBox.alert(Roo.encode(rdata));
46211                     return;
46212                 }
46213                 var data = rdata.data;
46214                 
46215                 if (this.uploadComplete) {
46216                    Roo.MessageBox.hide();
46217                    return;
46218                 }
46219                    
46220                 if (data){
46221                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46222                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46223                     );
46224                 }
46225                 this.uploadProgress.defer(2000,this);
46226             },
46227        
46228             failure: function(data) {
46229                 Roo.log('progress url failed ');
46230                 Roo.log(data);
46231             },
46232             scope : this
46233         });
46234            
46235     },
46236     
46237     
46238     run : function()
46239     {
46240         // run get Values on the form, so it syncs any secondary forms.
46241         this.form.getValues();
46242         
46243         var o = this.options;
46244         var method = this.getMethod();
46245         var isPost = method == 'POST';
46246         if(o.clientValidation === false || this.form.isValid()){
46247             
46248             if (this.form.progressUrl) {
46249                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46250                     (new Date() * 1) + '' + Math.random());
46251                     
46252             } 
46253             
46254             
46255             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46256                 form:this.form.el.dom,
46257                 url:this.getUrl(!isPost),
46258                 method: method,
46259                 params:isPost ? this.getParams() : null,
46260                 isUpload: this.form.fileUpload
46261             }));
46262             
46263             this.uploadProgress();
46264
46265         }else if (o.clientValidation !== false){ // client validation failed
46266             this.failureType = Roo.form.Action.CLIENT_INVALID;
46267             this.form.afterAction(this, false);
46268         }
46269     },
46270
46271     success : function(response)
46272     {
46273         this.uploadComplete= true;
46274         if (this.haveProgress) {
46275             Roo.MessageBox.hide();
46276         }
46277         
46278         
46279         var result = this.processResponse(response);
46280         if(result === true || result.success){
46281             this.form.afterAction(this, true);
46282             return;
46283         }
46284         if(result.errors){
46285             this.form.markInvalid(result.errors);
46286             this.failureType = Roo.form.Action.SERVER_INVALID;
46287         }
46288         this.form.afterAction(this, false);
46289     },
46290     failure : function(response)
46291     {
46292         this.uploadComplete= true;
46293         if (this.haveProgress) {
46294             Roo.MessageBox.hide();
46295         }
46296         
46297         this.response = response;
46298         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46299         this.form.afterAction(this, false);
46300     },
46301     
46302     handleResponse : function(response){
46303         if(this.form.errorReader){
46304             var rs = this.form.errorReader.read(response);
46305             var errors = [];
46306             if(rs.records){
46307                 for(var i = 0, len = rs.records.length; i < len; i++) {
46308                     var r = rs.records[i];
46309                     errors[i] = r.data;
46310                 }
46311             }
46312             if(errors.length < 1){
46313                 errors = null;
46314             }
46315             return {
46316                 success : rs.success,
46317                 errors : errors
46318             };
46319         }
46320         var ret = false;
46321         try {
46322             ret = Roo.decode(response.responseText);
46323         } catch (e) {
46324             ret = {
46325                 success: false,
46326                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46327                 errors : []
46328             };
46329         }
46330         return ret;
46331         
46332     }
46333 });
46334
46335
46336 Roo.form.Action.Load = function(form, options){
46337     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46338     this.reader = this.form.reader;
46339 };
46340
46341 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46342     type : 'load',
46343
46344     run : function(){
46345         
46346         Roo.Ajax.request(Roo.apply(
46347                 this.createCallback(), {
46348                     method:this.getMethod(),
46349                     url:this.getUrl(false),
46350                     params:this.getParams()
46351         }));
46352     },
46353
46354     success : function(response){
46355         
46356         var result = this.processResponse(response);
46357         if(result === true || !result.success || !result.data){
46358             this.failureType = Roo.form.Action.LOAD_FAILURE;
46359             this.form.afterAction(this, false);
46360             return;
46361         }
46362         this.form.clearInvalid();
46363         this.form.setValues(result.data);
46364         this.form.afterAction(this, true);
46365     },
46366
46367     handleResponse : function(response){
46368         if(this.form.reader){
46369             var rs = this.form.reader.read(response);
46370             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46371             return {
46372                 success : rs.success,
46373                 data : data
46374             };
46375         }
46376         return Roo.decode(response.responseText);
46377     }
46378 });
46379
46380 Roo.form.Action.ACTION_TYPES = {
46381     'load' : Roo.form.Action.Load,
46382     'submit' : Roo.form.Action.Submit
46383 };/*
46384  * Based on:
46385  * Ext JS Library 1.1.1
46386  * Copyright(c) 2006-2007, Ext JS, LLC.
46387  *
46388  * Originally Released Under LGPL - original licence link has changed is not relivant.
46389  *
46390  * Fork - LGPL
46391  * <script type="text/javascript">
46392  */
46393  
46394 /**
46395  * @class Roo.form.Layout
46396  * @extends Roo.Component
46397  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46398  * @constructor
46399  * @param {Object} config Configuration options
46400  */
46401 Roo.form.Layout = function(config){
46402     var xitems = [];
46403     if (config.items) {
46404         xitems = config.items;
46405         delete config.items;
46406     }
46407     Roo.form.Layout.superclass.constructor.call(this, config);
46408     this.stack = [];
46409     Roo.each(xitems, this.addxtype, this);
46410      
46411 };
46412
46413 Roo.extend(Roo.form.Layout, Roo.Component, {
46414     /**
46415      * @cfg {String/Object} autoCreate
46416      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46417      */
46418     /**
46419      * @cfg {String/Object/Function} style
46420      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46421      * a function which returns such a specification.
46422      */
46423     /**
46424      * @cfg {String} labelAlign
46425      * Valid values are "left," "top" and "right" (defaults to "left")
46426      */
46427     /**
46428      * @cfg {Number} labelWidth
46429      * Fixed width in pixels of all field labels (defaults to undefined)
46430      */
46431     /**
46432      * @cfg {Boolean} clear
46433      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46434      */
46435     clear : true,
46436     /**
46437      * @cfg {String} labelSeparator
46438      * The separator to use after field labels (defaults to ':')
46439      */
46440     labelSeparator : ':',
46441     /**
46442      * @cfg {Boolean} hideLabels
46443      * True to suppress the display of field labels in this layout (defaults to false)
46444      */
46445     hideLabels : false,
46446
46447     // private
46448     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46449     
46450     isLayout : true,
46451     
46452     // private
46453     onRender : function(ct, position){
46454         if(this.el){ // from markup
46455             this.el = Roo.get(this.el);
46456         }else {  // generate
46457             var cfg = this.getAutoCreate();
46458             this.el = ct.createChild(cfg, position);
46459         }
46460         if(this.style){
46461             this.el.applyStyles(this.style);
46462         }
46463         if(this.labelAlign){
46464             this.el.addClass('x-form-label-'+this.labelAlign);
46465         }
46466         if(this.hideLabels){
46467             this.labelStyle = "display:none";
46468             this.elementStyle = "padding-left:0;";
46469         }else{
46470             if(typeof this.labelWidth == 'number'){
46471                 this.labelStyle = "width:"+this.labelWidth+"px;";
46472                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46473             }
46474             if(this.labelAlign == 'top'){
46475                 this.labelStyle = "width:auto;";
46476                 this.elementStyle = "padding-left:0;";
46477             }
46478         }
46479         var stack = this.stack;
46480         var slen = stack.length;
46481         if(slen > 0){
46482             if(!this.fieldTpl){
46483                 var t = new Roo.Template(
46484                     '<div class="x-form-item {5}">',
46485                         '<label for="{0}" style="{2}">{1}{4}</label>',
46486                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46487                         '</div>',
46488                     '</div><div class="x-form-clear-left"></div>'
46489                 );
46490                 t.disableFormats = true;
46491                 t.compile();
46492                 Roo.form.Layout.prototype.fieldTpl = t;
46493             }
46494             for(var i = 0; i < slen; i++) {
46495                 if(stack[i].isFormField){
46496                     this.renderField(stack[i]);
46497                 }else{
46498                     this.renderComponent(stack[i]);
46499                 }
46500             }
46501         }
46502         if(this.clear){
46503             this.el.createChild({cls:'x-form-clear'});
46504         }
46505     },
46506
46507     // private
46508     renderField : function(f){
46509         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46510                f.id, //0
46511                f.fieldLabel, //1
46512                f.labelStyle||this.labelStyle||'', //2
46513                this.elementStyle||'', //3
46514                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46515                f.itemCls||this.itemCls||''  //5
46516        ], true).getPrevSibling());
46517     },
46518
46519     // private
46520     renderComponent : function(c){
46521         c.render(c.isLayout ? this.el : this.el.createChild());    
46522     },
46523     /**
46524      * Adds a object form elements (using the xtype property as the factory method.)
46525      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46526      * @param {Object} config 
46527      */
46528     addxtype : function(o)
46529     {
46530         // create the lement.
46531         o.form = this.form;
46532         var fe = Roo.factory(o, Roo.form);
46533         this.form.allItems.push(fe);
46534         this.stack.push(fe);
46535         
46536         if (fe.isFormField) {
46537             this.form.items.add(fe);
46538         }
46539          
46540         return fe;
46541     }
46542 });
46543
46544 /**
46545  * @class Roo.form.Column
46546  * @extends Roo.form.Layout
46547  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46548  * @constructor
46549  * @param {Object} config Configuration options
46550  */
46551 Roo.form.Column = function(config){
46552     Roo.form.Column.superclass.constructor.call(this, config);
46553 };
46554
46555 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46556     /**
46557      * @cfg {Number/String} width
46558      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46559      */
46560     /**
46561      * @cfg {String/Object} autoCreate
46562      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46563      */
46564
46565     // private
46566     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46567
46568     // private
46569     onRender : function(ct, position){
46570         Roo.form.Column.superclass.onRender.call(this, ct, position);
46571         if(this.width){
46572             this.el.setWidth(this.width);
46573         }
46574     }
46575 });
46576
46577
46578 /**
46579  * @class Roo.form.Row
46580  * @extends Roo.form.Layout
46581  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46582  * @constructor
46583  * @param {Object} config Configuration options
46584  */
46585
46586  
46587 Roo.form.Row = function(config){
46588     Roo.form.Row.superclass.constructor.call(this, config);
46589 };
46590  
46591 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46592       /**
46593      * @cfg {Number/String} width
46594      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46595      */
46596     /**
46597      * @cfg {Number/String} height
46598      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46599      */
46600     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46601     
46602     padWidth : 20,
46603     // private
46604     onRender : function(ct, position){
46605         //console.log('row render');
46606         if(!this.rowTpl){
46607             var t = new Roo.Template(
46608                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46609                     '<label for="{0}" style="{2}">{1}{4}</label>',
46610                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46611                     '</div>',
46612                 '</div>'
46613             );
46614             t.disableFormats = true;
46615             t.compile();
46616             Roo.form.Layout.prototype.rowTpl = t;
46617         }
46618         this.fieldTpl = this.rowTpl;
46619         
46620         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46621         var labelWidth = 100;
46622         
46623         if ((this.labelAlign != 'top')) {
46624             if (typeof this.labelWidth == 'number') {
46625                 labelWidth = this.labelWidth
46626             }
46627             this.padWidth =  20 + labelWidth;
46628             
46629         }
46630         
46631         Roo.form.Column.superclass.onRender.call(this, ct, position);
46632         if(this.width){
46633             this.el.setWidth(this.width);
46634         }
46635         if(this.height){
46636             this.el.setHeight(this.height);
46637         }
46638     },
46639     
46640     // private
46641     renderField : function(f){
46642         f.fieldEl = this.fieldTpl.append(this.el, [
46643                f.id, f.fieldLabel,
46644                f.labelStyle||this.labelStyle||'',
46645                this.elementStyle||'',
46646                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46647                f.itemCls||this.itemCls||'',
46648                f.width ? f.width + this.padWidth : 160 + this.padWidth
46649        ],true);
46650     }
46651 });
46652  
46653
46654 /**
46655  * @class Roo.form.FieldSet
46656  * @extends Roo.form.Layout
46657  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46658  * @constructor
46659  * @param {Object} config Configuration options
46660  */
46661 Roo.form.FieldSet = function(config){
46662     Roo.form.FieldSet.superclass.constructor.call(this, config);
46663 };
46664
46665 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46666     /**
46667      * @cfg {String} legend
46668      * The text to display as the legend for the FieldSet (defaults to '')
46669      */
46670     /**
46671      * @cfg {String/Object} autoCreate
46672      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46673      */
46674
46675     // private
46676     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46677
46678     // private
46679     onRender : function(ct, position){
46680         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46681         if(this.legend){
46682             this.setLegend(this.legend);
46683         }
46684     },
46685
46686     // private
46687     setLegend : function(text){
46688         if(this.rendered){
46689             this.el.child('legend').update(text);
46690         }
46691     }
46692 });/*
46693  * Based on:
46694  * Ext JS Library 1.1.1
46695  * Copyright(c) 2006-2007, Ext JS, LLC.
46696  *
46697  * Originally Released Under LGPL - original licence link has changed is not relivant.
46698  *
46699  * Fork - LGPL
46700  * <script type="text/javascript">
46701  */
46702 /**
46703  * @class Roo.form.VTypes
46704  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46705  * @singleton
46706  */
46707 Roo.form.VTypes = function(){
46708     // closure these in so they are only created once.
46709     var alpha = /^[a-zA-Z_]+$/;
46710     var alphanum = /^[a-zA-Z0-9_]+$/;
46711     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46712     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46713
46714     // All these messages and functions are configurable
46715     return {
46716         /**
46717          * The function used to validate email addresses
46718          * @param {String} value The email address
46719          */
46720         'email' : function(v){
46721             return email.test(v);
46722         },
46723         /**
46724          * The error text to display when the email validation function returns false
46725          * @type String
46726          */
46727         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46728         /**
46729          * The keystroke filter mask to be applied on email input
46730          * @type RegExp
46731          */
46732         'emailMask' : /[a-z0-9_\.\-@]/i,
46733
46734         /**
46735          * The function used to validate URLs
46736          * @param {String} value The URL
46737          */
46738         'url' : function(v){
46739             return url.test(v);
46740         },
46741         /**
46742          * The error text to display when the url validation function returns false
46743          * @type String
46744          */
46745         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46746         
46747         /**
46748          * The function used to validate alpha values
46749          * @param {String} value The value
46750          */
46751         'alpha' : function(v){
46752             return alpha.test(v);
46753         },
46754         /**
46755          * The error text to display when the alpha validation function returns false
46756          * @type String
46757          */
46758         'alphaText' : 'This field should only contain letters and _',
46759         /**
46760          * The keystroke filter mask to be applied on alpha input
46761          * @type RegExp
46762          */
46763         'alphaMask' : /[a-z_]/i,
46764
46765         /**
46766          * The function used to validate alphanumeric values
46767          * @param {String} value The value
46768          */
46769         'alphanum' : function(v){
46770             return alphanum.test(v);
46771         },
46772         /**
46773          * The error text to display when the alphanumeric validation function returns false
46774          * @type String
46775          */
46776         'alphanumText' : 'This field should only contain letters, numbers and _',
46777         /**
46778          * The keystroke filter mask to be applied on alphanumeric input
46779          * @type RegExp
46780          */
46781         'alphanumMask' : /[a-z0-9_]/i
46782     };
46783 }();//<script type="text/javascript">
46784
46785 /**
46786  * @class Roo.form.FCKeditor
46787  * @extends Roo.form.TextArea
46788  * Wrapper around the FCKEditor http://www.fckeditor.net
46789  * @constructor
46790  * Creates a new FCKeditor
46791  * @param {Object} config Configuration options
46792  */
46793 Roo.form.FCKeditor = function(config){
46794     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46795     this.addEvents({
46796          /**
46797          * @event editorinit
46798          * Fired when the editor is initialized - you can add extra handlers here..
46799          * @param {FCKeditor} this
46800          * @param {Object} the FCK object.
46801          */
46802         editorinit : true
46803     });
46804     
46805     
46806 };
46807 Roo.form.FCKeditor.editors = { };
46808 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46809 {
46810     //defaultAutoCreate : {
46811     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46812     //},
46813     // private
46814     /**
46815      * @cfg {Object} fck options - see fck manual for details.
46816      */
46817     fckconfig : false,
46818     
46819     /**
46820      * @cfg {Object} fck toolbar set (Basic or Default)
46821      */
46822     toolbarSet : 'Basic',
46823     /**
46824      * @cfg {Object} fck BasePath
46825      */ 
46826     basePath : '/fckeditor/',
46827     
46828     
46829     frame : false,
46830     
46831     value : '',
46832     
46833    
46834     onRender : function(ct, position)
46835     {
46836         if(!this.el){
46837             this.defaultAutoCreate = {
46838                 tag: "textarea",
46839                 style:"width:300px;height:60px;",
46840                 autocomplete: "new-password"
46841             };
46842         }
46843         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46844         /*
46845         if(this.grow){
46846             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46847             if(this.preventScrollbars){
46848                 this.el.setStyle("overflow", "hidden");
46849             }
46850             this.el.setHeight(this.growMin);
46851         }
46852         */
46853         //console.log('onrender' + this.getId() );
46854         Roo.form.FCKeditor.editors[this.getId()] = this;
46855          
46856
46857         this.replaceTextarea() ;
46858         
46859     },
46860     
46861     getEditor : function() {
46862         return this.fckEditor;
46863     },
46864     /**
46865      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46866      * @param {Mixed} value The value to set
46867      */
46868     
46869     
46870     setValue : function(value)
46871     {
46872         //console.log('setValue: ' + value);
46873         
46874         if(typeof(value) == 'undefined') { // not sure why this is happending...
46875             return;
46876         }
46877         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46878         
46879         //if(!this.el || !this.getEditor()) {
46880         //    this.value = value;
46881             //this.setValue.defer(100,this,[value]);    
46882         //    return;
46883         //} 
46884         
46885         if(!this.getEditor()) {
46886             return;
46887         }
46888         
46889         this.getEditor().SetData(value);
46890         
46891         //
46892
46893     },
46894
46895     /**
46896      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46897      * @return {Mixed} value The field value
46898      */
46899     getValue : function()
46900     {
46901         
46902         if (this.frame && this.frame.dom.style.display == 'none') {
46903             return Roo.form.FCKeditor.superclass.getValue.call(this);
46904         }
46905         
46906         if(!this.el || !this.getEditor()) {
46907            
46908            // this.getValue.defer(100,this); 
46909             return this.value;
46910         }
46911        
46912         
46913         var value=this.getEditor().GetData();
46914         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46915         return Roo.form.FCKeditor.superclass.getValue.call(this);
46916         
46917
46918     },
46919
46920     /**
46921      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46922      * @return {Mixed} value The field value
46923      */
46924     getRawValue : function()
46925     {
46926         if (this.frame && this.frame.dom.style.display == 'none') {
46927             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46928         }
46929         
46930         if(!this.el || !this.getEditor()) {
46931             //this.getRawValue.defer(100,this); 
46932             return this.value;
46933             return;
46934         }
46935         
46936         
46937         
46938         var value=this.getEditor().GetData();
46939         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46940         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46941          
46942     },
46943     
46944     setSize : function(w,h) {
46945         
46946         
46947         
46948         //if (this.frame && this.frame.dom.style.display == 'none') {
46949         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46950         //    return;
46951         //}
46952         //if(!this.el || !this.getEditor()) {
46953         //    this.setSize.defer(100,this, [w,h]); 
46954         //    return;
46955         //}
46956         
46957         
46958         
46959         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46960         
46961         this.frame.dom.setAttribute('width', w);
46962         this.frame.dom.setAttribute('height', h);
46963         this.frame.setSize(w,h);
46964         
46965     },
46966     
46967     toggleSourceEdit : function(value) {
46968         
46969       
46970          
46971         this.el.dom.style.display = value ? '' : 'none';
46972         this.frame.dom.style.display = value ?  'none' : '';
46973         
46974     },
46975     
46976     
46977     focus: function(tag)
46978     {
46979         if (this.frame.dom.style.display == 'none') {
46980             return Roo.form.FCKeditor.superclass.focus.call(this);
46981         }
46982         if(!this.el || !this.getEditor()) {
46983             this.focus.defer(100,this, [tag]); 
46984             return;
46985         }
46986         
46987         
46988         
46989         
46990         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46991         this.getEditor().Focus();
46992         if (tgs.length) {
46993             if (!this.getEditor().Selection.GetSelection()) {
46994                 this.focus.defer(100,this, [tag]); 
46995                 return;
46996             }
46997             
46998             
46999             var r = this.getEditor().EditorDocument.createRange();
47000             r.setStart(tgs[0],0);
47001             r.setEnd(tgs[0],0);
47002             this.getEditor().Selection.GetSelection().removeAllRanges();
47003             this.getEditor().Selection.GetSelection().addRange(r);
47004             this.getEditor().Focus();
47005         }
47006         
47007     },
47008     
47009     
47010     
47011     replaceTextarea : function()
47012     {
47013         if ( document.getElementById( this.getId() + '___Frame' ) )
47014             return ;
47015         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
47016         //{
47017             // We must check the elements firstly using the Id and then the name.
47018         var oTextarea = document.getElementById( this.getId() );
47019         
47020         var colElementsByName = document.getElementsByName( this.getId() ) ;
47021          
47022         oTextarea.style.display = 'none' ;
47023
47024         if ( oTextarea.tabIndex ) {            
47025             this.TabIndex = oTextarea.tabIndex ;
47026         }
47027         
47028         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47029         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47030         this.frame = Roo.get(this.getId() + '___Frame')
47031     },
47032     
47033     _getConfigHtml : function()
47034     {
47035         var sConfig = '' ;
47036
47037         for ( var o in this.fckconfig ) {
47038             sConfig += sConfig.length > 0  ? '&amp;' : '';
47039             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47040         }
47041
47042         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47043     },
47044     
47045     
47046     _getIFrameHtml : function()
47047     {
47048         var sFile = 'fckeditor.html' ;
47049         /* no idea what this is about..
47050         try
47051         {
47052             if ( (/fcksource=true/i).test( window.top.location.search ) )
47053                 sFile = 'fckeditor.original.html' ;
47054         }
47055         catch (e) { 
47056         */
47057
47058         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47059         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47060         
47061         
47062         var html = '<iframe id="' + this.getId() +
47063             '___Frame" src="' + sLink +
47064             '" width="' + this.width +
47065             '" height="' + this.height + '"' +
47066             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47067             ' frameborder="0" scrolling="no"></iframe>' ;
47068
47069         return html ;
47070     },
47071     
47072     _insertHtmlBefore : function( html, element )
47073     {
47074         if ( element.insertAdjacentHTML )       {
47075             // IE
47076             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47077         } else { // Gecko
47078             var oRange = document.createRange() ;
47079             oRange.setStartBefore( element ) ;
47080             var oFragment = oRange.createContextualFragment( html );
47081             element.parentNode.insertBefore( oFragment, element ) ;
47082         }
47083     }
47084     
47085     
47086   
47087     
47088     
47089     
47090     
47091
47092 });
47093
47094 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47095
47096 function FCKeditor_OnComplete(editorInstance){
47097     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47098     f.fckEditor = editorInstance;
47099     //console.log("loaded");
47100     f.fireEvent('editorinit', f, editorInstance);
47101
47102   
47103
47104  
47105
47106
47107
47108
47109
47110
47111
47112
47113
47114
47115
47116
47117
47118
47119
47120 //<script type="text/javascript">
47121 /**
47122  * @class Roo.form.GridField
47123  * @extends Roo.form.Field
47124  * Embed a grid (or editable grid into a form)
47125  * STATUS ALPHA
47126  * 
47127  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47128  * it needs 
47129  * xgrid.store = Roo.data.Store
47130  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47131  * xgrid.store.reader = Roo.data.JsonReader 
47132  * 
47133  * 
47134  * @constructor
47135  * Creates a new GridField
47136  * @param {Object} config Configuration options
47137  */
47138 Roo.form.GridField = function(config){
47139     Roo.form.GridField.superclass.constructor.call(this, config);
47140      
47141 };
47142
47143 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47144     /**
47145      * @cfg {Number} width  - used to restrict width of grid..
47146      */
47147     width : 100,
47148     /**
47149      * @cfg {Number} height - used to restrict height of grid..
47150      */
47151     height : 50,
47152      /**
47153      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47154          * 
47155          *}
47156      */
47157     xgrid : false, 
47158     /**
47159      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47160      * {tag: "input", type: "checkbox", autocomplete: "off"})
47161      */
47162    // defaultAutoCreate : { tag: 'div' },
47163     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47164     /**
47165      * @cfg {String} addTitle Text to include for adding a title.
47166      */
47167     addTitle : false,
47168     //
47169     onResize : function(){
47170         Roo.form.Field.superclass.onResize.apply(this, arguments);
47171     },
47172
47173     initEvents : function(){
47174         // Roo.form.Checkbox.superclass.initEvents.call(this);
47175         // has no events...
47176        
47177     },
47178
47179
47180     getResizeEl : function(){
47181         return this.wrap;
47182     },
47183
47184     getPositionEl : function(){
47185         return this.wrap;
47186     },
47187
47188     // private
47189     onRender : function(ct, position){
47190         
47191         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47192         var style = this.style;
47193         delete this.style;
47194         
47195         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47196         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47197         this.viewEl = this.wrap.createChild({ tag: 'div' });
47198         if (style) {
47199             this.viewEl.applyStyles(style);
47200         }
47201         if (this.width) {
47202             this.viewEl.setWidth(this.width);
47203         }
47204         if (this.height) {
47205             this.viewEl.setHeight(this.height);
47206         }
47207         //if(this.inputValue !== undefined){
47208         //this.setValue(this.value);
47209         
47210         
47211         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47212         
47213         
47214         this.grid.render();
47215         this.grid.getDataSource().on('remove', this.refreshValue, this);
47216         this.grid.getDataSource().on('update', this.refreshValue, this);
47217         this.grid.on('afteredit', this.refreshValue, this);
47218  
47219     },
47220      
47221     
47222     /**
47223      * Sets the value of the item. 
47224      * @param {String} either an object  or a string..
47225      */
47226     setValue : function(v){
47227         //this.value = v;
47228         v = v || []; // empty set..
47229         // this does not seem smart - it really only affects memoryproxy grids..
47230         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47231             var ds = this.grid.getDataSource();
47232             // assumes a json reader..
47233             var data = {}
47234             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47235             ds.loadData( data);
47236         }
47237         // clear selection so it does not get stale.
47238         if (this.grid.sm) { 
47239             this.grid.sm.clearSelections();
47240         }
47241         
47242         Roo.form.GridField.superclass.setValue.call(this, v);
47243         this.refreshValue();
47244         // should load data in the grid really....
47245     },
47246     
47247     // private
47248     refreshValue: function() {
47249          var val = [];
47250         this.grid.getDataSource().each(function(r) {
47251             val.push(r.data);
47252         });
47253         this.el.dom.value = Roo.encode(val);
47254     }
47255     
47256      
47257     
47258     
47259 });/*
47260  * Based on:
47261  * Ext JS Library 1.1.1
47262  * Copyright(c) 2006-2007, Ext JS, LLC.
47263  *
47264  * Originally Released Under LGPL - original licence link has changed is not relivant.
47265  *
47266  * Fork - LGPL
47267  * <script type="text/javascript">
47268  */
47269 /**
47270  * @class Roo.form.DisplayField
47271  * @extends Roo.form.Field
47272  * A generic Field to display non-editable data.
47273  * @constructor
47274  * Creates a new Display Field item.
47275  * @param {Object} config Configuration options
47276  */
47277 Roo.form.DisplayField = function(config){
47278     Roo.form.DisplayField.superclass.constructor.call(this, config);
47279     
47280 };
47281
47282 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47283     inputType:      'hidden',
47284     allowBlank:     true,
47285     readOnly:         true,
47286     
47287  
47288     /**
47289      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47290      */
47291     focusClass : undefined,
47292     /**
47293      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47294      */
47295     fieldClass: 'x-form-field',
47296     
47297      /**
47298      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47299      */
47300     valueRenderer: undefined,
47301     
47302     width: 100,
47303     /**
47304      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47305      * {tag: "input", type: "checkbox", autocomplete: "off"})
47306      */
47307      
47308  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47309
47310     onResize : function(){
47311         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47312         
47313     },
47314
47315     initEvents : function(){
47316         // Roo.form.Checkbox.superclass.initEvents.call(this);
47317         // has no events...
47318        
47319     },
47320
47321
47322     getResizeEl : function(){
47323         return this.wrap;
47324     },
47325
47326     getPositionEl : function(){
47327         return this.wrap;
47328     },
47329
47330     // private
47331     onRender : function(ct, position){
47332         
47333         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47334         //if(this.inputValue !== undefined){
47335         this.wrap = this.el.wrap();
47336         
47337         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47338         
47339         if (this.bodyStyle) {
47340             this.viewEl.applyStyles(this.bodyStyle);
47341         }
47342         //this.viewEl.setStyle('padding', '2px');
47343         
47344         this.setValue(this.value);
47345         
47346     },
47347 /*
47348     // private
47349     initValue : Roo.emptyFn,
47350
47351   */
47352
47353         // private
47354     onClick : function(){
47355         
47356     },
47357
47358     /**
47359      * Sets the checked state of the checkbox.
47360      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47361      */
47362     setValue : function(v){
47363         this.value = v;
47364         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47365         // this might be called before we have a dom element..
47366         if (!this.viewEl) {
47367             return;
47368         }
47369         this.viewEl.dom.innerHTML = html;
47370         Roo.form.DisplayField.superclass.setValue.call(this, v);
47371
47372     }
47373 });/*
47374  * 
47375  * Licence- LGPL
47376  * 
47377  */
47378
47379 /**
47380  * @class Roo.form.DayPicker
47381  * @extends Roo.form.Field
47382  * A Day picker show [M] [T] [W] ....
47383  * @constructor
47384  * Creates a new Day Picker
47385  * @param {Object} config Configuration options
47386  */
47387 Roo.form.DayPicker= function(config){
47388     Roo.form.DayPicker.superclass.constructor.call(this, config);
47389      
47390 };
47391
47392 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47393     /**
47394      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47395      */
47396     focusClass : undefined,
47397     /**
47398      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47399      */
47400     fieldClass: "x-form-field",
47401    
47402     /**
47403      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47404      * {tag: "input", type: "checkbox", autocomplete: "off"})
47405      */
47406     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47407     
47408    
47409     actionMode : 'viewEl', 
47410     //
47411     // private
47412  
47413     inputType : 'hidden',
47414     
47415      
47416     inputElement: false, // real input element?
47417     basedOn: false, // ????
47418     
47419     isFormField: true, // not sure where this is needed!!!!
47420
47421     onResize : function(){
47422         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47423         if(!this.boxLabel){
47424             this.el.alignTo(this.wrap, 'c-c');
47425         }
47426     },
47427
47428     initEvents : function(){
47429         Roo.form.Checkbox.superclass.initEvents.call(this);
47430         this.el.on("click", this.onClick,  this);
47431         this.el.on("change", this.onClick,  this);
47432     },
47433
47434
47435     getResizeEl : function(){
47436         return this.wrap;
47437     },
47438
47439     getPositionEl : function(){
47440         return this.wrap;
47441     },
47442
47443     
47444     // private
47445     onRender : function(ct, position){
47446         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47447        
47448         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47449         
47450         var r1 = '<table><tr>';
47451         var r2 = '<tr class="x-form-daypick-icons">';
47452         for (var i=0; i < 7; i++) {
47453             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47454             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47455         }
47456         
47457         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47458         viewEl.select('img').on('click', this.onClick, this);
47459         this.viewEl = viewEl;   
47460         
47461         
47462         // this will not work on Chrome!!!
47463         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47464         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47465         
47466         
47467           
47468
47469     },
47470
47471     // private
47472     initValue : Roo.emptyFn,
47473
47474     /**
47475      * Returns the checked state of the checkbox.
47476      * @return {Boolean} True if checked, else false
47477      */
47478     getValue : function(){
47479         return this.el.dom.value;
47480         
47481     },
47482
47483         // private
47484     onClick : function(e){ 
47485         //this.setChecked(!this.checked);
47486         Roo.get(e.target).toggleClass('x-menu-item-checked');
47487         this.refreshValue();
47488         //if(this.el.dom.checked != this.checked){
47489         //    this.setValue(this.el.dom.checked);
47490        // }
47491     },
47492     
47493     // private
47494     refreshValue : function()
47495     {
47496         var val = '';
47497         this.viewEl.select('img',true).each(function(e,i,n)  {
47498             val += e.is(".x-menu-item-checked") ? String(n) : '';
47499         });
47500         this.setValue(val, true);
47501     },
47502
47503     /**
47504      * Sets the checked state of the checkbox.
47505      * On is always based on a string comparison between inputValue and the param.
47506      * @param {Boolean/String} value - the value to set 
47507      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47508      */
47509     setValue : function(v,suppressEvent){
47510         if (!this.el.dom) {
47511             return;
47512         }
47513         var old = this.el.dom.value ;
47514         this.el.dom.value = v;
47515         if (suppressEvent) {
47516             return ;
47517         }
47518          
47519         // update display..
47520         this.viewEl.select('img',true).each(function(e,i,n)  {
47521             
47522             var on = e.is(".x-menu-item-checked");
47523             var newv = v.indexOf(String(n)) > -1;
47524             if (on != newv) {
47525                 e.toggleClass('x-menu-item-checked');
47526             }
47527             
47528         });
47529         
47530         
47531         this.fireEvent('change', this, v, old);
47532         
47533         
47534     },
47535    
47536     // handle setting of hidden value by some other method!!?!?
47537     setFromHidden: function()
47538     {
47539         if(!this.el){
47540             return;
47541         }
47542         //console.log("SET FROM HIDDEN");
47543         //alert('setFrom hidden');
47544         this.setValue(this.el.dom.value);
47545     },
47546     
47547     onDestroy : function()
47548     {
47549         if(this.viewEl){
47550             Roo.get(this.viewEl).remove();
47551         }
47552          
47553         Roo.form.DayPicker.superclass.onDestroy.call(this);
47554     }
47555
47556 });/*
47557  * RooJS Library 1.1.1
47558  * Copyright(c) 2008-2011  Alan Knowles
47559  *
47560  * License - LGPL
47561  */
47562  
47563
47564 /**
47565  * @class Roo.form.ComboCheck
47566  * @extends Roo.form.ComboBox
47567  * A combobox for multiple select items.
47568  *
47569  * FIXME - could do with a reset button..
47570  * 
47571  * @constructor
47572  * Create a new ComboCheck
47573  * @param {Object} config Configuration options
47574  */
47575 Roo.form.ComboCheck = function(config){
47576     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47577     // should verify some data...
47578     // like
47579     // hiddenName = required..
47580     // displayField = required
47581     // valudField == required
47582     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47583     var _t = this;
47584     Roo.each(req, function(e) {
47585         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47586             throw "Roo.form.ComboCheck : missing value for: " + e;
47587         }
47588     });
47589     
47590     
47591 };
47592
47593 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47594      
47595      
47596     editable : false,
47597      
47598     selectedClass: 'x-menu-item-checked', 
47599     
47600     // private
47601     onRender : function(ct, position){
47602         var _t = this;
47603         
47604         
47605         
47606         if(!this.tpl){
47607             var cls = 'x-combo-list';
47608
47609             
47610             this.tpl =  new Roo.Template({
47611                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47612                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47613                    '<span>{' + this.displayField + '}</span>' +
47614                     '</div>' 
47615                 
47616             });
47617         }
47618  
47619         
47620         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47621         this.view.singleSelect = false;
47622         this.view.multiSelect = true;
47623         this.view.toggleSelect = true;
47624         this.pageTb.add(new Roo.Toolbar.Fill(), {
47625             
47626             text: 'Done',
47627             handler: function()
47628             {
47629                 _t.collapse();
47630             }
47631         });
47632     },
47633     
47634     onViewOver : function(e, t){
47635         // do nothing...
47636         return;
47637         
47638     },
47639     
47640     onViewClick : function(doFocus,index){
47641         return;
47642         
47643     },
47644     select: function () {
47645         //Roo.log("SELECT CALLED");
47646     },
47647      
47648     selectByValue : function(xv, scrollIntoView){
47649         var ar = this.getValueArray();
47650         var sels = [];
47651         
47652         Roo.each(ar, function(v) {
47653             if(v === undefined || v === null){
47654                 return;
47655             }
47656             var r = this.findRecord(this.valueField, v);
47657             if(r){
47658                 sels.push(this.store.indexOf(r))
47659                 
47660             }
47661         },this);
47662         this.view.select(sels);
47663         return false;
47664     },
47665     
47666     
47667     
47668     onSelect : function(record, index){
47669        // Roo.log("onselect Called");
47670        // this is only called by the clear button now..
47671         this.view.clearSelections();
47672         this.setValue('[]');
47673         if (this.value != this.valueBefore) {
47674             this.fireEvent('change', this, this.value, this.valueBefore);
47675             this.valueBefore = this.value;
47676         }
47677     },
47678     getValueArray : function()
47679     {
47680         var ar = [] ;
47681         
47682         try {
47683             //Roo.log(this.value);
47684             if (typeof(this.value) == 'undefined') {
47685                 return [];
47686             }
47687             var ar = Roo.decode(this.value);
47688             return  ar instanceof Array ? ar : []; //?? valid?
47689             
47690         } catch(e) {
47691             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47692             return [];
47693         }
47694          
47695     },
47696     expand : function ()
47697     {
47698         
47699         Roo.form.ComboCheck.superclass.expand.call(this);
47700         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47701         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47702         
47703
47704     },
47705     
47706     collapse : function(){
47707         Roo.form.ComboCheck.superclass.collapse.call(this);
47708         var sl = this.view.getSelectedIndexes();
47709         var st = this.store;
47710         var nv = [];
47711         var tv = [];
47712         var r;
47713         Roo.each(sl, function(i) {
47714             r = st.getAt(i);
47715             nv.push(r.get(this.valueField));
47716         },this);
47717         this.setValue(Roo.encode(nv));
47718         if (this.value != this.valueBefore) {
47719
47720             this.fireEvent('change', this, this.value, this.valueBefore);
47721             this.valueBefore = this.value;
47722         }
47723         
47724     },
47725     
47726     setValue : function(v){
47727         // Roo.log(v);
47728         this.value = v;
47729         
47730         var vals = this.getValueArray();
47731         var tv = [];
47732         Roo.each(vals, function(k) {
47733             var r = this.findRecord(this.valueField, k);
47734             if(r){
47735                 tv.push(r.data[this.displayField]);
47736             }else if(this.valueNotFoundText !== undefined){
47737                 tv.push( this.valueNotFoundText );
47738             }
47739         },this);
47740        // Roo.log(tv);
47741         
47742         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47743         this.hiddenField.value = v;
47744         this.value = v;
47745     }
47746     
47747 });/*
47748  * Based on:
47749  * Ext JS Library 1.1.1
47750  * Copyright(c) 2006-2007, Ext JS, LLC.
47751  *
47752  * Originally Released Under LGPL - original licence link has changed is not relivant.
47753  *
47754  * Fork - LGPL
47755  * <script type="text/javascript">
47756  */
47757  
47758 /**
47759  * @class Roo.form.Signature
47760  * @extends Roo.form.Field
47761  * Signature field.  
47762  * @constructor
47763  * 
47764  * @param {Object} config Configuration options
47765  */
47766
47767 Roo.form.Signature = function(config){
47768     Roo.form.Signature.superclass.constructor.call(this, config);
47769     
47770     this.addEvents({// not in used??
47771          /**
47772          * @event confirm
47773          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47774              * @param {Roo.form.Signature} combo This combo box
47775              */
47776         'confirm' : true,
47777         /**
47778          * @event reset
47779          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47780              * @param {Roo.form.ComboBox} combo This combo box
47781              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47782              */
47783         'reset' : true
47784     });
47785 };
47786
47787 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47788     /**
47789      * @cfg {Object} labels Label to use when rendering a form.
47790      * defaults to 
47791      * labels : { 
47792      *      clear : "Clear",
47793      *      confirm : "Confirm"
47794      *  }
47795      */
47796     labels : { 
47797         clear : "Clear",
47798         confirm : "Confirm"
47799     },
47800     /**
47801      * @cfg {Number} width The signature panel width (defaults to 300)
47802      */
47803     width: 300,
47804     /**
47805      * @cfg {Number} height The signature panel height (defaults to 100)
47806      */
47807     height : 100,
47808     /**
47809      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47810      */
47811     allowBlank : false,
47812     
47813     //private
47814     // {Object} signPanel The signature SVG panel element (defaults to {})
47815     signPanel : {},
47816     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47817     isMouseDown : false,
47818     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47819     isConfirmed : false,
47820     // {String} signatureTmp SVG mapping string (defaults to empty string)
47821     signatureTmp : '',
47822     
47823     
47824     defaultAutoCreate : { // modified by initCompnoent..
47825         tag: "input",
47826         type:"hidden"
47827     },
47828
47829     // private
47830     onRender : function(ct, position){
47831         
47832         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47833         
47834         this.wrap = this.el.wrap({
47835             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47836         });
47837         
47838         this.createToolbar(this);
47839         this.signPanel = this.wrap.createChild({
47840                 tag: 'div',
47841                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47842             }, this.el
47843         );
47844             
47845         this.svgID = Roo.id();
47846         this.svgEl = this.signPanel.createChild({
47847               xmlns : 'http://www.w3.org/2000/svg',
47848               tag : 'svg',
47849               id : this.svgID + "-svg",
47850               width: this.width,
47851               height: this.height,
47852               viewBox: '0 0 '+this.width+' '+this.height,
47853               cn : [
47854                 {
47855                     tag: "rect",
47856                     id: this.svgID + "-svg-r",
47857                     width: this.width,
47858                     height: this.height,
47859                     fill: "#ffa"
47860                 },
47861                 {
47862                     tag: "line",
47863                     id: this.svgID + "-svg-l",
47864                     x1: "0", // start
47865                     y1: (this.height*0.8), // start set the line in 80% of height
47866                     x2: this.width, // end
47867                     y2: (this.height*0.8), // end set the line in 80% of height
47868                     'stroke': "#666",
47869                     'stroke-width': "1",
47870                     'stroke-dasharray': "3",
47871                     'shape-rendering': "crispEdges",
47872                     'pointer-events': "none"
47873                 },
47874                 {
47875                     tag: "path",
47876                     id: this.svgID + "-svg-p",
47877                     'stroke': "navy",
47878                     'stroke-width': "3",
47879                     'fill': "none",
47880                     'pointer-events': 'none'
47881                 }
47882               ]
47883         });
47884         this.createSVG();
47885         this.svgBox = this.svgEl.dom.getScreenCTM();
47886     },
47887     createSVG : function(){ 
47888         var svg = this.signPanel;
47889         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47890         var t = this;
47891
47892         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47893         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47894         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47895         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47896         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47897         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47898         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47899         
47900     },
47901     isTouchEvent : function(e){
47902         return e.type.match(/^touch/);
47903     },
47904     getCoords : function (e) {
47905         var pt    = this.svgEl.dom.createSVGPoint();
47906         pt.x = e.clientX; 
47907         pt.y = e.clientY;
47908         if (this.isTouchEvent(e)) {
47909             pt.x =  e.targetTouches[0].clientX;
47910             pt.y = e.targetTouches[0].clientY;
47911         }
47912         var a = this.svgEl.dom.getScreenCTM();
47913         var b = a.inverse();
47914         var mx = pt.matrixTransform(b);
47915         return mx.x + ',' + mx.y;
47916     },
47917     //mouse event headler 
47918     down : function (e) {
47919         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47920         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47921         
47922         this.isMouseDown = true;
47923         
47924         e.preventDefault();
47925     },
47926     move : function (e) {
47927         if (this.isMouseDown) {
47928             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47929             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47930         }
47931         
47932         e.preventDefault();
47933     },
47934     up : function (e) {
47935         this.isMouseDown = false;
47936         var sp = this.signatureTmp.split(' ');
47937         
47938         if(sp.length > 1){
47939             if(!sp[sp.length-2].match(/^L/)){
47940                 sp.pop();
47941                 sp.pop();
47942                 sp.push("");
47943                 this.signatureTmp = sp.join(" ");
47944             }
47945         }
47946         if(this.getValue() != this.signatureTmp){
47947             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47948             this.isConfirmed = false;
47949         }
47950         e.preventDefault();
47951     },
47952     
47953     /**
47954      * Protected method that will not generally be called directly. It
47955      * is called when the editor creates its toolbar. Override this method if you need to
47956      * add custom toolbar buttons.
47957      * @param {HtmlEditor} editor
47958      */
47959     createToolbar : function(editor){
47960          function btn(id, toggle, handler){
47961             var xid = fid + '-'+ id ;
47962             return {
47963                 id : xid,
47964                 cmd : id,
47965                 cls : 'x-btn-icon x-edit-'+id,
47966                 enableToggle:toggle !== false,
47967                 scope: editor, // was editor...
47968                 handler:handler||editor.relayBtnCmd,
47969                 clickEvent:'mousedown',
47970                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47971                 tabIndex:-1
47972             };
47973         }
47974         
47975         
47976         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47977         this.tb = tb;
47978         this.tb.add(
47979            {
47980                 cls : ' x-signature-btn x-signature-'+id,
47981                 scope: editor, // was editor...
47982                 handler: this.reset,
47983                 clickEvent:'mousedown',
47984                 text: this.labels.clear
47985             },
47986             {
47987                  xtype : 'Fill',
47988                  xns: Roo.Toolbar
47989             }, 
47990             {
47991                 cls : '  x-signature-btn x-signature-'+id,
47992                 scope: editor, // was editor...
47993                 handler: this.confirmHandler,
47994                 clickEvent:'mousedown',
47995                 text: this.labels.confirm
47996             }
47997         );
47998     
47999     },
48000     //public
48001     /**
48002      * when user is clicked confirm then show this image.....
48003      * 
48004      * @return {String} Image Data URI
48005      */
48006     getImageDataURI : function(){
48007         var svg = this.svgEl.dom.parentNode.innerHTML;
48008         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
48009         return src; 
48010     },
48011     /**
48012      * 
48013      * @return {Boolean} this.isConfirmed
48014      */
48015     getConfirmed : function(){
48016         return this.isConfirmed;
48017     },
48018     /**
48019      * 
48020      * @return {Number} this.width
48021      */
48022     getWidth : function(){
48023         return this.width;
48024     },
48025     /**
48026      * 
48027      * @return {Number} this.height
48028      */
48029     getHeight : function(){
48030         return this.height;
48031     },
48032     // private
48033     getSignature : function(){
48034         return this.signatureTmp;
48035     },
48036     // private
48037     reset : function(){
48038         this.signatureTmp = '';
48039         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48040         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48041         this.isConfirmed = false;
48042         Roo.form.Signature.superclass.reset.call(this);
48043     },
48044     setSignature : function(s){
48045         this.signatureTmp = s;
48046         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48047         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48048         this.setValue(s);
48049         this.isConfirmed = false;
48050         Roo.form.Signature.superclass.reset.call(this);
48051     }, 
48052     test : function(){
48053 //        Roo.log(this.signPanel.dom.contentWindow.up())
48054     },
48055     //private
48056     setConfirmed : function(){
48057         
48058         
48059         
48060 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48061     },
48062     // private
48063     confirmHandler : function(){
48064         if(!this.getSignature()){
48065             return;
48066         }
48067         
48068         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48069         this.setValue(this.getSignature());
48070         this.isConfirmed = true;
48071         
48072         this.fireEvent('confirm', this);
48073     },
48074     // private
48075     // Subclasses should provide the validation implementation by overriding this
48076     validateValue : function(value){
48077         if(this.allowBlank){
48078             return true;
48079         }
48080         
48081         if(this.isConfirmed){
48082             return true;
48083         }
48084         return false;
48085     }
48086 });/*
48087  * Based on:
48088  * Ext JS Library 1.1.1
48089  * Copyright(c) 2006-2007, Ext JS, LLC.
48090  *
48091  * Originally Released Under LGPL - original licence link has changed is not relivant.
48092  *
48093  * Fork - LGPL
48094  * <script type="text/javascript">
48095  */
48096  
48097
48098 /**
48099  * @class Roo.form.ComboBox
48100  * @extends Roo.form.TriggerField
48101  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48102  * @constructor
48103  * Create a new ComboBox.
48104  * @param {Object} config Configuration options
48105  */
48106 Roo.form.Select = function(config){
48107     Roo.form.Select.superclass.constructor.call(this, config);
48108      
48109 };
48110
48111 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48112     /**
48113      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48114      */
48115     /**
48116      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48117      * rendering into an Roo.Editor, defaults to false)
48118      */
48119     /**
48120      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48121      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48122      */
48123     /**
48124      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48125      */
48126     /**
48127      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48128      * the dropdown list (defaults to undefined, with no header element)
48129      */
48130
48131      /**
48132      * @cfg {String/Roo.Template} tpl The template to use to render the output
48133      */
48134      
48135     // private
48136     defaultAutoCreate : {tag: "select"  },
48137     /**
48138      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48139      */
48140     listWidth: undefined,
48141     /**
48142      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48143      * mode = 'remote' or 'text' if mode = 'local')
48144      */
48145     displayField: undefined,
48146     /**
48147      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48148      * mode = 'remote' or 'value' if mode = 'local'). 
48149      * Note: use of a valueField requires the user make a selection
48150      * in order for a value to be mapped.
48151      */
48152     valueField: undefined,
48153     
48154     
48155     /**
48156      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48157      * field's data value (defaults to the underlying DOM element's name)
48158      */
48159     hiddenName: undefined,
48160     /**
48161      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48162      */
48163     listClass: '',
48164     /**
48165      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48166      */
48167     selectedClass: 'x-combo-selected',
48168     /**
48169      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48170      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48171      * which displays a downward arrow icon).
48172      */
48173     triggerClass : 'x-form-arrow-trigger',
48174     /**
48175      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48176      */
48177     shadow:'sides',
48178     /**
48179      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48180      * anchor positions (defaults to 'tl-bl')
48181      */
48182     listAlign: 'tl-bl?',
48183     /**
48184      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48185      */
48186     maxHeight: 300,
48187     /**
48188      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48189      * query specified by the allQuery config option (defaults to 'query')
48190      */
48191     triggerAction: 'query',
48192     /**
48193      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48194      * (defaults to 4, does not apply if editable = false)
48195      */
48196     minChars : 4,
48197     /**
48198      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48199      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48200      */
48201     typeAhead: false,
48202     /**
48203      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48204      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48205      */
48206     queryDelay: 500,
48207     /**
48208      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48209      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48210      */
48211     pageSize: 0,
48212     /**
48213      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48214      * when editable = true (defaults to false)
48215      */
48216     selectOnFocus:false,
48217     /**
48218      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48219      */
48220     queryParam: 'query',
48221     /**
48222      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48223      * when mode = 'remote' (defaults to 'Loading...')
48224      */
48225     loadingText: 'Loading...',
48226     /**
48227      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48228      */
48229     resizable: false,
48230     /**
48231      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48232      */
48233     handleHeight : 8,
48234     /**
48235      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48236      * traditional select (defaults to true)
48237      */
48238     editable: true,
48239     /**
48240      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48241      */
48242     allQuery: '',
48243     /**
48244      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48245      */
48246     mode: 'remote',
48247     /**
48248      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48249      * listWidth has a higher value)
48250      */
48251     minListWidth : 70,
48252     /**
48253      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48254      * allow the user to set arbitrary text into the field (defaults to false)
48255      */
48256     forceSelection:false,
48257     /**
48258      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48259      * if typeAhead = true (defaults to 250)
48260      */
48261     typeAheadDelay : 250,
48262     /**
48263      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48264      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48265      */
48266     valueNotFoundText : undefined,
48267     
48268     /**
48269      * @cfg {String} defaultValue The value displayed after loading the store.
48270      */
48271     defaultValue: '',
48272     
48273     /**
48274      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48275      */
48276     blockFocus : false,
48277     
48278     /**
48279      * @cfg {Boolean} disableClear Disable showing of clear button.
48280      */
48281     disableClear : false,
48282     /**
48283      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48284      */
48285     alwaysQuery : false,
48286     
48287     //private
48288     addicon : false,
48289     editicon: false,
48290     
48291     // element that contains real text value.. (when hidden is used..)
48292      
48293     // private
48294     onRender : function(ct, position){
48295         Roo.form.Field.prototype.onRender.call(this, ct, position);
48296         
48297         if(this.store){
48298             this.store.on('beforeload', this.onBeforeLoad, this);
48299             this.store.on('load', this.onLoad, this);
48300             this.store.on('loadexception', this.onLoadException, this);
48301             this.store.load({});
48302         }
48303         
48304         
48305         
48306     },
48307
48308     // private
48309     initEvents : function(){
48310         //Roo.form.ComboBox.superclass.initEvents.call(this);
48311  
48312     },
48313
48314     onDestroy : function(){
48315        
48316         if(this.store){
48317             this.store.un('beforeload', this.onBeforeLoad, this);
48318             this.store.un('load', this.onLoad, this);
48319             this.store.un('loadexception', this.onLoadException, this);
48320         }
48321         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48322     },
48323
48324     // private
48325     fireKey : function(e){
48326         if(e.isNavKeyPress() && !this.list.isVisible()){
48327             this.fireEvent("specialkey", this, e);
48328         }
48329     },
48330
48331     // private
48332     onResize: function(w, h){
48333         
48334         return; 
48335     
48336         
48337     },
48338
48339     /**
48340      * Allow or prevent the user from directly editing the field text.  If false is passed,
48341      * the user will only be able to select from the items defined in the dropdown list.  This method
48342      * is the runtime equivalent of setting the 'editable' config option at config time.
48343      * @param {Boolean} value True to allow the user to directly edit the field text
48344      */
48345     setEditable : function(value){
48346          
48347     },
48348
48349     // private
48350     onBeforeLoad : function(){
48351         
48352         Roo.log("Select before load");
48353         return;
48354     
48355         this.innerList.update(this.loadingText ?
48356                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48357         //this.restrictHeight();
48358         this.selectedIndex = -1;
48359     },
48360
48361     // private
48362     onLoad : function(){
48363
48364     
48365         var dom = this.el.dom;
48366         dom.innerHTML = '';
48367          var od = dom.ownerDocument;
48368          
48369         if (this.emptyText) {
48370             var op = od.createElement('option');
48371             op.setAttribute('value', '');
48372             op.innerHTML = String.format('{0}', this.emptyText);
48373             dom.appendChild(op);
48374         }
48375         if(this.store.getCount() > 0){
48376            
48377             var vf = this.valueField;
48378             var df = this.displayField;
48379             this.store.data.each(function(r) {
48380                 // which colmsn to use... testing - cdoe / title..
48381                 var op = od.createElement('option');
48382                 op.setAttribute('value', r.data[vf]);
48383                 op.innerHTML = String.format('{0}', r.data[df]);
48384                 dom.appendChild(op);
48385             });
48386             if (typeof(this.defaultValue != 'undefined')) {
48387                 this.setValue(this.defaultValue);
48388             }
48389             
48390              
48391         }else{
48392             //this.onEmptyResults();
48393         }
48394         //this.el.focus();
48395     },
48396     // private
48397     onLoadException : function()
48398     {
48399         dom.innerHTML = '';
48400             
48401         Roo.log("Select on load exception");
48402         return;
48403     
48404         this.collapse();
48405         Roo.log(this.store.reader.jsonData);
48406         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48407             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48408         }
48409         
48410         
48411     },
48412     // private
48413     onTypeAhead : function(){
48414          
48415     },
48416
48417     // private
48418     onSelect : function(record, index){
48419         Roo.log('on select?');
48420         return;
48421         if(this.fireEvent('beforeselect', this, record, index) !== false){
48422             this.setFromData(index > -1 ? record.data : false);
48423             this.collapse();
48424             this.fireEvent('select', this, record, index);
48425         }
48426     },
48427
48428     /**
48429      * Returns the currently selected field value or empty string if no value is set.
48430      * @return {String} value The selected value
48431      */
48432     getValue : function(){
48433         var dom = this.el.dom;
48434         this.value = dom.options[dom.selectedIndex].value;
48435         return this.value;
48436         
48437     },
48438
48439     /**
48440      * Clears any text/value currently set in the field
48441      */
48442     clearValue : function(){
48443         this.value = '';
48444         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48445         
48446     },
48447
48448     /**
48449      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48450      * will be displayed in the field.  If the value does not match the data value of an existing item,
48451      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48452      * Otherwise the field will be blank (although the value will still be set).
48453      * @param {String} value The value to match
48454      */
48455     setValue : function(v){
48456         var d = this.el.dom;
48457         for (var i =0; i < d.options.length;i++) {
48458             if (v == d.options[i].value) {
48459                 d.selectedIndex = i;
48460                 this.value = v;
48461                 return;
48462             }
48463         }
48464         this.clearValue();
48465     },
48466     /**
48467      * @property {Object} the last set data for the element
48468      */
48469     
48470     lastData : false,
48471     /**
48472      * Sets the value of the field based on a object which is related to the record format for the store.
48473      * @param {Object} value the value to set as. or false on reset?
48474      */
48475     setFromData : function(o){
48476         Roo.log('setfrom data?');
48477          
48478         
48479         
48480     },
48481     // private
48482     reset : function(){
48483         this.clearValue();
48484     },
48485     // private
48486     findRecord : function(prop, value){
48487         
48488         return false;
48489     
48490         var record;
48491         if(this.store.getCount() > 0){
48492             this.store.each(function(r){
48493                 if(r.data[prop] == value){
48494                     record = r;
48495                     return false;
48496                 }
48497                 return true;
48498             });
48499         }
48500         return record;
48501     },
48502     
48503     getName: function()
48504     {
48505         // returns hidden if it's set..
48506         if (!this.rendered) {return ''};
48507         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48508         
48509     },
48510      
48511
48512     
48513
48514     // private
48515     onEmptyResults : function(){
48516         Roo.log('empty results');
48517         //this.collapse();
48518     },
48519
48520     /**
48521      * Returns true if the dropdown list is expanded, else false.
48522      */
48523     isExpanded : function(){
48524         return false;
48525     },
48526
48527     /**
48528      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48529      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48530      * @param {String} value The data value of the item to select
48531      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48532      * selected item if it is not currently in view (defaults to true)
48533      * @return {Boolean} True if the value matched an item in the list, else false
48534      */
48535     selectByValue : function(v, scrollIntoView){
48536         Roo.log('select By Value');
48537         return false;
48538     
48539         if(v !== undefined && v !== null){
48540             var r = this.findRecord(this.valueField || this.displayField, v);
48541             if(r){
48542                 this.select(this.store.indexOf(r), scrollIntoView);
48543                 return true;
48544             }
48545         }
48546         return false;
48547     },
48548
48549     /**
48550      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48551      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48552      * @param {Number} index The zero-based index of the list item to select
48553      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48554      * selected item if it is not currently in view (defaults to true)
48555      */
48556     select : function(index, scrollIntoView){
48557         Roo.log('select ');
48558         return  ;
48559         
48560         this.selectedIndex = index;
48561         this.view.select(index);
48562         if(scrollIntoView !== false){
48563             var el = this.view.getNode(index);
48564             if(el){
48565                 this.innerList.scrollChildIntoView(el, false);
48566             }
48567         }
48568     },
48569
48570       
48571
48572     // private
48573     validateBlur : function(){
48574         
48575         return;
48576         
48577     },
48578
48579     // private
48580     initQuery : function(){
48581         this.doQuery(this.getRawValue());
48582     },
48583
48584     // private
48585     doForce : function(){
48586         if(this.el.dom.value.length > 0){
48587             this.el.dom.value =
48588                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48589              
48590         }
48591     },
48592
48593     /**
48594      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48595      * query allowing the query action to be canceled if needed.
48596      * @param {String} query The SQL query to execute
48597      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48598      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48599      * saved in the current store (defaults to false)
48600      */
48601     doQuery : function(q, forceAll){
48602         
48603         Roo.log('doQuery?');
48604         if(q === undefined || q === null){
48605             q = '';
48606         }
48607         var qe = {
48608             query: q,
48609             forceAll: forceAll,
48610             combo: this,
48611             cancel:false
48612         };
48613         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48614             return false;
48615         }
48616         q = qe.query;
48617         forceAll = qe.forceAll;
48618         if(forceAll === true || (q.length >= this.minChars)){
48619             if(this.lastQuery != q || this.alwaysQuery){
48620                 this.lastQuery = q;
48621                 if(this.mode == 'local'){
48622                     this.selectedIndex = -1;
48623                     if(forceAll){
48624                         this.store.clearFilter();
48625                     }else{
48626                         this.store.filter(this.displayField, q);
48627                     }
48628                     this.onLoad();
48629                 }else{
48630                     this.store.baseParams[this.queryParam] = q;
48631                     this.store.load({
48632                         params: this.getParams(q)
48633                     });
48634                     this.expand();
48635                 }
48636             }else{
48637                 this.selectedIndex = -1;
48638                 this.onLoad();   
48639             }
48640         }
48641     },
48642
48643     // private
48644     getParams : function(q){
48645         var p = {};
48646         //p[this.queryParam] = q;
48647         if(this.pageSize){
48648             p.start = 0;
48649             p.limit = this.pageSize;
48650         }
48651         return p;
48652     },
48653
48654     /**
48655      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48656      */
48657     collapse : function(){
48658         
48659     },
48660
48661     // private
48662     collapseIf : function(e){
48663         
48664     },
48665
48666     /**
48667      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48668      */
48669     expand : function(){
48670         
48671     } ,
48672
48673     // private
48674      
48675
48676     /** 
48677     * @cfg {Boolean} grow 
48678     * @hide 
48679     */
48680     /** 
48681     * @cfg {Number} growMin 
48682     * @hide 
48683     */
48684     /** 
48685     * @cfg {Number} growMax 
48686     * @hide 
48687     */
48688     /**
48689      * @hide
48690      * @method autoSize
48691      */
48692     
48693     setWidth : function()
48694     {
48695         
48696     },
48697     getResizeEl : function(){
48698         return this.el;
48699     }
48700 });//<script type="text/javasscript">
48701  
48702
48703 /**
48704  * @class Roo.DDView
48705  * A DnD enabled version of Roo.View.
48706  * @param {Element/String} container The Element in which to create the View.
48707  * @param {String} tpl The template string used to create the markup for each element of the View
48708  * @param {Object} config The configuration properties. These include all the config options of
48709  * {@link Roo.View} plus some specific to this class.<br>
48710  * <p>
48711  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48712  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48713  * <p>
48714  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48715 .x-view-drag-insert-above {
48716         border-top:1px dotted #3366cc;
48717 }
48718 .x-view-drag-insert-below {
48719         border-bottom:1px dotted #3366cc;
48720 }
48721 </code></pre>
48722  * 
48723  */
48724  
48725 Roo.DDView = function(container, tpl, config) {
48726     Roo.DDView.superclass.constructor.apply(this, arguments);
48727     this.getEl().setStyle("outline", "0px none");
48728     this.getEl().unselectable();
48729     if (this.dragGroup) {
48730                 this.setDraggable(this.dragGroup.split(","));
48731     }
48732     if (this.dropGroup) {
48733                 this.setDroppable(this.dropGroup.split(","));
48734     }
48735     if (this.deletable) {
48736         this.setDeletable();
48737     }
48738     this.isDirtyFlag = false;
48739         this.addEvents({
48740                 "drop" : true
48741         });
48742 };
48743
48744 Roo.extend(Roo.DDView, Roo.View, {
48745 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48746 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48747 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48748 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48749
48750         isFormField: true,
48751
48752         reset: Roo.emptyFn,
48753         
48754         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48755
48756         validate: function() {
48757                 return true;
48758         },
48759         
48760         destroy: function() {
48761                 this.purgeListeners();
48762                 this.getEl.removeAllListeners();
48763                 this.getEl().remove();
48764                 if (this.dragZone) {
48765                         if (this.dragZone.destroy) {
48766                                 this.dragZone.destroy();
48767                         }
48768                 }
48769                 if (this.dropZone) {
48770                         if (this.dropZone.destroy) {
48771                                 this.dropZone.destroy();
48772                         }
48773                 }
48774         },
48775
48776 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48777         getName: function() {
48778                 return this.name;
48779         },
48780
48781 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48782         setValue: function(v) {
48783                 if (!this.store) {
48784                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48785                 }
48786                 var data = {};
48787                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48788                 this.store.proxy = new Roo.data.MemoryProxy(data);
48789                 this.store.load();
48790         },
48791
48792 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48793         getValue: function() {
48794                 var result = '(';
48795                 this.store.each(function(rec) {
48796                         result += rec.id + ',';
48797                 });
48798                 return result.substr(0, result.length - 1) + ')';
48799         },
48800         
48801         getIds: function() {
48802                 var i = 0, result = new Array(this.store.getCount());
48803                 this.store.each(function(rec) {
48804                         result[i++] = rec.id;
48805                 });
48806                 return result;
48807         },
48808         
48809         isDirty: function() {
48810                 return this.isDirtyFlag;
48811         },
48812
48813 /**
48814  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48815  *      whole Element becomes the target, and this causes the drop gesture to append.
48816  */
48817     getTargetFromEvent : function(e) {
48818                 var target = e.getTarget();
48819                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48820                 target = target.parentNode;
48821                 }
48822                 if (!target) {
48823                         target = this.el.dom.lastChild || this.el.dom;
48824                 }
48825                 return target;
48826     },
48827
48828 /**
48829  *      Create the drag data which consists of an object which has the property "ddel" as
48830  *      the drag proxy element. 
48831  */
48832     getDragData : function(e) {
48833         var target = this.findItemFromChild(e.getTarget());
48834                 if(target) {
48835                         this.handleSelection(e);
48836                         var selNodes = this.getSelectedNodes();
48837             var dragData = {
48838                 source: this,
48839                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48840                 nodes: selNodes,
48841                 records: []
48842                         };
48843                         var selectedIndices = this.getSelectedIndexes();
48844                         for (var i = 0; i < selectedIndices.length; i++) {
48845                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48846                         }
48847                         if (selNodes.length == 1) {
48848                                 dragData.ddel = target.cloneNode(true); // the div element
48849                         } else {
48850                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48851                                 div.className = 'multi-proxy';
48852                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48853                                         div.appendChild(selNodes[i].cloneNode(true));
48854                                 }
48855                                 dragData.ddel = div;
48856                         }
48857             //console.log(dragData)
48858             //console.log(dragData.ddel.innerHTML)
48859                         return dragData;
48860                 }
48861         //console.log('nodragData')
48862                 return false;
48863     },
48864     
48865 /**     Specify to which ddGroup items in this DDView may be dragged. */
48866     setDraggable: function(ddGroup) {
48867         if (ddGroup instanceof Array) {
48868                 Roo.each(ddGroup, this.setDraggable, this);
48869                 return;
48870         }
48871         if (this.dragZone) {
48872                 this.dragZone.addToGroup(ddGroup);
48873         } else {
48874                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48875                                 containerScroll: true,
48876                                 ddGroup: ddGroup 
48877
48878                         });
48879 //                      Draggability implies selection. DragZone's mousedown selects the element.
48880                         if (!this.multiSelect) { this.singleSelect = true; }
48881
48882 //                      Wire the DragZone's handlers up to methods in *this*
48883                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48884                 }
48885     },
48886
48887 /**     Specify from which ddGroup this DDView accepts drops. */
48888     setDroppable: function(ddGroup) {
48889         if (ddGroup instanceof Array) {
48890                 Roo.each(ddGroup, this.setDroppable, this);
48891                 return;
48892         }
48893         if (this.dropZone) {
48894                 this.dropZone.addToGroup(ddGroup);
48895         } else {
48896                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48897                                 containerScroll: true,
48898                                 ddGroup: ddGroup
48899                         });
48900
48901 //                      Wire the DropZone's handlers up to methods in *this*
48902                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48903                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48904                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48905                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48906                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48907                 }
48908     },
48909
48910 /**     Decide whether to drop above or below a View node. */
48911     getDropPoint : function(e, n, dd){
48912         if (n == this.el.dom) { return "above"; }
48913                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48914                 var c = t + (b - t) / 2;
48915                 var y = Roo.lib.Event.getPageY(e);
48916                 if(y <= c) {
48917                         return "above";
48918                 }else{
48919                         return "below";
48920                 }
48921     },
48922
48923     onNodeEnter : function(n, dd, e, data){
48924                 return false;
48925     },
48926     
48927     onNodeOver : function(n, dd, e, data){
48928                 var pt = this.getDropPoint(e, n, dd);
48929                 // set the insert point style on the target node
48930                 var dragElClass = this.dropNotAllowed;
48931                 if (pt) {
48932                         var targetElClass;
48933                         if (pt == "above"){
48934                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48935                                 targetElClass = "x-view-drag-insert-above";
48936                         } else {
48937                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48938                                 targetElClass = "x-view-drag-insert-below";
48939                         }
48940                         if (this.lastInsertClass != targetElClass){
48941                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48942                                 this.lastInsertClass = targetElClass;
48943                         }
48944                 }
48945                 return dragElClass;
48946         },
48947
48948     onNodeOut : function(n, dd, e, data){
48949                 this.removeDropIndicators(n);
48950     },
48951
48952     onNodeDrop : function(n, dd, e, data){
48953         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48954                 return false;
48955         }
48956         var pt = this.getDropPoint(e, n, dd);
48957                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48958                 if (pt == "below") { insertAt++; }
48959                 for (var i = 0; i < data.records.length; i++) {
48960                         var r = data.records[i];
48961                         var dup = this.store.getById(r.id);
48962                         if (dup && (dd != this.dragZone)) {
48963                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48964                         } else {
48965                                 if (data.copy) {
48966                                         this.store.insert(insertAt++, r.copy());
48967                                 } else {
48968                                         data.source.isDirtyFlag = true;
48969                                         r.store.remove(r);
48970                                         this.store.insert(insertAt++, r);
48971                                 }
48972                                 this.isDirtyFlag = true;
48973                         }
48974                 }
48975                 this.dragZone.cachedTarget = null;
48976                 return true;
48977     },
48978
48979     removeDropIndicators : function(n){
48980                 if(n){
48981                         Roo.fly(n).removeClass([
48982                                 "x-view-drag-insert-above",
48983                                 "x-view-drag-insert-below"]);
48984                         this.lastInsertClass = "_noclass";
48985                 }
48986     },
48987
48988 /**
48989  *      Utility method. Add a delete option to the DDView's context menu.
48990  *      @param {String} imageUrl The URL of the "delete" icon image.
48991  */
48992         setDeletable: function(imageUrl) {
48993                 if (!this.singleSelect && !this.multiSelect) {
48994                         this.singleSelect = true;
48995                 }
48996                 var c = this.getContextMenu();
48997                 this.contextMenu.on("itemclick", function(item) {
48998                         switch (item.id) {
48999                                 case "delete":
49000                                         this.remove(this.getSelectedIndexes());
49001                                         break;
49002                         }
49003                 }, this);
49004                 this.contextMenu.add({
49005                         icon: imageUrl,
49006                         id: "delete",
49007                         text: 'Delete'
49008                 });
49009         },
49010         
49011 /**     Return the context menu for this DDView. */
49012         getContextMenu: function() {
49013                 if (!this.contextMenu) {
49014 //                      Create the View's context menu
49015                         this.contextMenu = new Roo.menu.Menu({
49016                                 id: this.id + "-contextmenu"
49017                         });
49018                         this.el.on("contextmenu", this.showContextMenu, this);
49019                 }
49020                 return this.contextMenu;
49021         },
49022         
49023         disableContextMenu: function() {
49024                 if (this.contextMenu) {
49025                         this.el.un("contextmenu", this.showContextMenu, this);
49026                 }
49027         },
49028
49029         showContextMenu: function(e, item) {
49030         item = this.findItemFromChild(e.getTarget());
49031                 if (item) {
49032                         e.stopEvent();
49033                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49034                         this.contextMenu.showAt(e.getXY());
49035             }
49036     },
49037
49038 /**
49039  *      Remove {@link Roo.data.Record}s at the specified indices.
49040  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49041  */
49042     remove: function(selectedIndices) {
49043                 selectedIndices = [].concat(selectedIndices);
49044                 for (var i = 0; i < selectedIndices.length; i++) {
49045                         var rec = this.store.getAt(selectedIndices[i]);
49046                         this.store.remove(rec);
49047                 }
49048     },
49049
49050 /**
49051  *      Double click fires the event, but also, if this is draggable, and there is only one other
49052  *      related DropZone, it transfers the selected node.
49053  */
49054     onDblClick : function(e){
49055         var item = this.findItemFromChild(e.getTarget());
49056         if(item){
49057             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49058                 return false;
49059             }
49060             if (this.dragGroup) {
49061                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49062                     while (targets.indexOf(this.dropZone) > -1) {
49063                             targets.remove(this.dropZone);
49064                                 }
49065                     if (targets.length == 1) {
49066                                         this.dragZone.cachedTarget = null;
49067                         var el = Roo.get(targets[0].getEl());
49068                         var box = el.getBox(true);
49069                         targets[0].onNodeDrop(el.dom, {
49070                                 target: el.dom,
49071                                 xy: [box.x, box.y + box.height - 1]
49072                         }, null, this.getDragData(e));
49073                     }
49074                 }
49075         }
49076     },
49077     
49078     handleSelection: function(e) {
49079                 this.dragZone.cachedTarget = null;
49080         var item = this.findItemFromChild(e.getTarget());
49081         if (!item) {
49082                 this.clearSelections(true);
49083                 return;
49084         }
49085                 if (item && (this.multiSelect || this.singleSelect)){
49086                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49087                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49088                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49089                                 this.unselect(item);
49090                         } else {
49091                                 this.select(item, this.multiSelect && e.ctrlKey);
49092                                 this.lastSelection = item;
49093                         }
49094                 }
49095     },
49096
49097     onItemClick : function(item, index, e){
49098                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49099                         return false;
49100                 }
49101                 return true;
49102     },
49103
49104     unselect : function(nodeInfo, suppressEvent){
49105                 var node = this.getNode(nodeInfo);
49106                 if(node && this.isSelected(node)){
49107                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49108                                 Roo.fly(node).removeClass(this.selectedClass);
49109                                 this.selections.remove(node);
49110                                 if(!suppressEvent){
49111                                         this.fireEvent("selectionchange", this, this.selections);
49112                                 }
49113                         }
49114                 }
49115     }
49116 });
49117 /*
49118  * Based on:
49119  * Ext JS Library 1.1.1
49120  * Copyright(c) 2006-2007, Ext JS, LLC.
49121  *
49122  * Originally Released Under LGPL - original licence link has changed is not relivant.
49123  *
49124  * Fork - LGPL
49125  * <script type="text/javascript">
49126  */
49127  
49128 /**
49129  * @class Roo.LayoutManager
49130  * @extends Roo.util.Observable
49131  * Base class for layout managers.
49132  */
49133 Roo.LayoutManager = function(container, config){
49134     Roo.LayoutManager.superclass.constructor.call(this);
49135     this.el = Roo.get(container);
49136     // ie scrollbar fix
49137     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49138         document.body.scroll = "no";
49139     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49140         this.el.position('relative');
49141     }
49142     this.id = this.el.id;
49143     this.el.addClass("x-layout-container");
49144     /** false to disable window resize monitoring @type Boolean */
49145     this.monitorWindowResize = true;
49146     this.regions = {};
49147     this.addEvents({
49148         /**
49149          * @event layout
49150          * Fires when a layout is performed. 
49151          * @param {Roo.LayoutManager} this
49152          */
49153         "layout" : true,
49154         /**
49155          * @event regionresized
49156          * Fires when the user resizes a region. 
49157          * @param {Roo.LayoutRegion} region The resized region
49158          * @param {Number} newSize The new size (width for east/west, height for north/south)
49159          */
49160         "regionresized" : true,
49161         /**
49162          * @event regioncollapsed
49163          * Fires when a region is collapsed. 
49164          * @param {Roo.LayoutRegion} region The collapsed region
49165          */
49166         "regioncollapsed" : true,
49167         /**
49168          * @event regionexpanded
49169          * Fires when a region is expanded.  
49170          * @param {Roo.LayoutRegion} region The expanded region
49171          */
49172         "regionexpanded" : true
49173     });
49174     this.updating = false;
49175     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49176 };
49177
49178 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49179     /**
49180      * Returns true if this layout is currently being updated
49181      * @return {Boolean}
49182      */
49183     isUpdating : function(){
49184         return this.updating; 
49185     },
49186     
49187     /**
49188      * Suspend the LayoutManager from doing auto-layouts while
49189      * making multiple add or remove calls
49190      */
49191     beginUpdate : function(){
49192         this.updating = true;    
49193     },
49194     
49195     /**
49196      * Restore auto-layouts and optionally disable the manager from performing a layout
49197      * @param {Boolean} noLayout true to disable a layout update 
49198      */
49199     endUpdate : function(noLayout){
49200         this.updating = false;
49201         if(!noLayout){
49202             this.layout();
49203         }    
49204     },
49205     
49206     layout: function(){
49207         
49208     },
49209     
49210     onRegionResized : function(region, newSize){
49211         this.fireEvent("regionresized", region, newSize);
49212         this.layout();
49213     },
49214     
49215     onRegionCollapsed : function(region){
49216         this.fireEvent("regioncollapsed", region);
49217     },
49218     
49219     onRegionExpanded : function(region){
49220         this.fireEvent("regionexpanded", region);
49221     },
49222         
49223     /**
49224      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49225      * performs box-model adjustments.
49226      * @return {Object} The size as an object {width: (the width), height: (the height)}
49227      */
49228     getViewSize : function(){
49229         var size;
49230         if(this.el.dom != document.body){
49231             size = this.el.getSize();
49232         }else{
49233             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49234         }
49235         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49236         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49237         return size;
49238     },
49239     
49240     /**
49241      * Returns the Element this layout is bound to.
49242      * @return {Roo.Element}
49243      */
49244     getEl : function(){
49245         return this.el;
49246     },
49247     
49248     /**
49249      * Returns the specified region.
49250      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49251      * @return {Roo.LayoutRegion}
49252      */
49253     getRegion : function(target){
49254         return this.regions[target.toLowerCase()];
49255     },
49256     
49257     onWindowResize : function(){
49258         if(this.monitorWindowResize){
49259             this.layout();
49260         }
49261     }
49262 });/*
49263  * Based on:
49264  * Ext JS Library 1.1.1
49265  * Copyright(c) 2006-2007, Ext JS, LLC.
49266  *
49267  * Originally Released Under LGPL - original licence link has changed is not relivant.
49268  *
49269  * Fork - LGPL
49270  * <script type="text/javascript">
49271  */
49272 /**
49273  * @class Roo.BorderLayout
49274  * @extends Roo.LayoutManager
49275  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49276  * please see: <br><br>
49277  * <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>
49278  * <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>
49279  * Example:
49280  <pre><code>
49281  var layout = new Roo.BorderLayout(document.body, {
49282     north: {
49283         initialSize: 25,
49284         titlebar: false
49285     },
49286     west: {
49287         split:true,
49288         initialSize: 200,
49289         minSize: 175,
49290         maxSize: 400,
49291         titlebar: true,
49292         collapsible: true
49293     },
49294     east: {
49295         split:true,
49296         initialSize: 202,
49297         minSize: 175,
49298         maxSize: 400,
49299         titlebar: true,
49300         collapsible: true
49301     },
49302     south: {
49303         split:true,
49304         initialSize: 100,
49305         minSize: 100,
49306         maxSize: 200,
49307         titlebar: true,
49308         collapsible: true
49309     },
49310     center: {
49311         titlebar: true,
49312         autoScroll:true,
49313         resizeTabs: true,
49314         minTabWidth: 50,
49315         preferredTabWidth: 150
49316     }
49317 });
49318
49319 // shorthand
49320 var CP = Roo.ContentPanel;
49321
49322 layout.beginUpdate();
49323 layout.add("north", new CP("north", "North"));
49324 layout.add("south", new CP("south", {title: "South", closable: true}));
49325 layout.add("west", new CP("west", {title: "West"}));
49326 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49327 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49328 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49329 layout.getRegion("center").showPanel("center1");
49330 layout.endUpdate();
49331 </code></pre>
49332
49333 <b>The container the layout is rendered into can be either the body element or any other element.
49334 If it is not the body element, the container needs to either be an absolute positioned element,
49335 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49336 the container size if it is not the body element.</b>
49337
49338 * @constructor
49339 * Create a new BorderLayout
49340 * @param {String/HTMLElement/Element} container The container this layout is bound to
49341 * @param {Object} config Configuration options
49342  */
49343 Roo.BorderLayout = function(container, config){
49344     config = config || {};
49345     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49346     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49347     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49348         var target = this.factory.validRegions[i];
49349         if(config[target]){
49350             this.addRegion(target, config[target]);
49351         }
49352     }
49353 };
49354
49355 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49356     /**
49357      * Creates and adds a new region if it doesn't already exist.
49358      * @param {String} target The target region key (north, south, east, west or center).
49359      * @param {Object} config The regions config object
49360      * @return {BorderLayoutRegion} The new region
49361      */
49362     addRegion : function(target, config){
49363         if(!this.regions[target]){
49364             var r = this.factory.create(target, this, config);
49365             this.bindRegion(target, r);
49366         }
49367         return this.regions[target];
49368     },
49369
49370     // private (kinda)
49371     bindRegion : function(name, r){
49372         this.regions[name] = r;
49373         r.on("visibilitychange", this.layout, this);
49374         r.on("paneladded", this.layout, this);
49375         r.on("panelremoved", this.layout, this);
49376         r.on("invalidated", this.layout, this);
49377         r.on("resized", this.onRegionResized, this);
49378         r.on("collapsed", this.onRegionCollapsed, this);
49379         r.on("expanded", this.onRegionExpanded, this);
49380     },
49381
49382     /**
49383      * Performs a layout update.
49384      */
49385     layout : function(){
49386         if(this.updating) return;
49387         var size = this.getViewSize();
49388         var w = size.width;
49389         var h = size.height;
49390         var centerW = w;
49391         var centerH = h;
49392         var centerY = 0;
49393         var centerX = 0;
49394         //var x = 0, y = 0;
49395
49396         var rs = this.regions;
49397         var north = rs["north"];
49398         var south = rs["south"]; 
49399         var west = rs["west"];
49400         var east = rs["east"];
49401         var center = rs["center"];
49402         //if(this.hideOnLayout){ // not supported anymore
49403             //c.el.setStyle("display", "none");
49404         //}
49405         if(north && north.isVisible()){
49406             var b = north.getBox();
49407             var m = north.getMargins();
49408             b.width = w - (m.left+m.right);
49409             b.x = m.left;
49410             b.y = m.top;
49411             centerY = b.height + b.y + m.bottom;
49412             centerH -= centerY;
49413             north.updateBox(this.safeBox(b));
49414         }
49415         if(south && south.isVisible()){
49416             var b = south.getBox();
49417             var m = south.getMargins();
49418             b.width = w - (m.left+m.right);
49419             b.x = m.left;
49420             var totalHeight = (b.height + m.top + m.bottom);
49421             b.y = h - totalHeight + m.top;
49422             centerH -= totalHeight;
49423             south.updateBox(this.safeBox(b));
49424         }
49425         if(west && west.isVisible()){
49426             var b = west.getBox();
49427             var m = west.getMargins();
49428             b.height = centerH - (m.top+m.bottom);
49429             b.x = m.left;
49430             b.y = centerY + m.top;
49431             var totalWidth = (b.width + m.left + m.right);
49432             centerX += totalWidth;
49433             centerW -= totalWidth;
49434             west.updateBox(this.safeBox(b));
49435         }
49436         if(east && east.isVisible()){
49437             var b = east.getBox();
49438             var m = east.getMargins();
49439             b.height = centerH - (m.top+m.bottom);
49440             var totalWidth = (b.width + m.left + m.right);
49441             b.x = w - totalWidth + m.left;
49442             b.y = centerY + m.top;
49443             centerW -= totalWidth;
49444             east.updateBox(this.safeBox(b));
49445         }
49446         if(center){
49447             var m = center.getMargins();
49448             var centerBox = {
49449                 x: centerX + m.left,
49450                 y: centerY + m.top,
49451                 width: centerW - (m.left+m.right),
49452                 height: centerH - (m.top+m.bottom)
49453             };
49454             //if(this.hideOnLayout){
49455                 //center.el.setStyle("display", "block");
49456             //}
49457             center.updateBox(this.safeBox(centerBox));
49458         }
49459         this.el.repaint();
49460         this.fireEvent("layout", this);
49461     },
49462
49463     // private
49464     safeBox : function(box){
49465         box.width = Math.max(0, box.width);
49466         box.height = Math.max(0, box.height);
49467         return box;
49468     },
49469
49470     /**
49471      * Adds a ContentPanel (or subclass) to this layout.
49472      * @param {String} target The target region key (north, south, east, west or center).
49473      * @param {Roo.ContentPanel} panel The panel to add
49474      * @return {Roo.ContentPanel} The added panel
49475      */
49476     add : function(target, panel){
49477          
49478         target = target.toLowerCase();
49479         return this.regions[target].add(panel);
49480     },
49481
49482     /**
49483      * Remove a ContentPanel (or subclass) to this layout.
49484      * @param {String} target The target region key (north, south, east, west or center).
49485      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49486      * @return {Roo.ContentPanel} The removed panel
49487      */
49488     remove : function(target, panel){
49489         target = target.toLowerCase();
49490         return this.regions[target].remove(panel);
49491     },
49492
49493     /**
49494      * Searches all regions for a panel with the specified id
49495      * @param {String} panelId
49496      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49497      */
49498     findPanel : function(panelId){
49499         var rs = this.regions;
49500         for(var target in rs){
49501             if(typeof rs[target] != "function"){
49502                 var p = rs[target].getPanel(panelId);
49503                 if(p){
49504                     return p;
49505                 }
49506             }
49507         }
49508         return null;
49509     },
49510
49511     /**
49512      * Searches all regions for a panel with the specified id and activates (shows) it.
49513      * @param {String/ContentPanel} panelId The panels id or the panel itself
49514      * @return {Roo.ContentPanel} The shown panel or null
49515      */
49516     showPanel : function(panelId) {
49517       var rs = this.regions;
49518       for(var target in rs){
49519          var r = rs[target];
49520          if(typeof r != "function"){
49521             if(r.hasPanel(panelId)){
49522                return r.showPanel(panelId);
49523             }
49524          }
49525       }
49526       return null;
49527    },
49528
49529    /**
49530      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49531      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49532      */
49533     restoreState : function(provider){
49534         if(!provider){
49535             provider = Roo.state.Manager;
49536         }
49537         var sm = new Roo.LayoutStateManager();
49538         sm.init(this, provider);
49539     },
49540
49541     /**
49542      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49543      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49544      * a valid ContentPanel config object.  Example:
49545      * <pre><code>
49546 // Create the main layout
49547 var layout = new Roo.BorderLayout('main-ct', {
49548     west: {
49549         split:true,
49550         minSize: 175,
49551         titlebar: true
49552     },
49553     center: {
49554         title:'Components'
49555     }
49556 }, 'main-ct');
49557
49558 // Create and add multiple ContentPanels at once via configs
49559 layout.batchAdd({
49560    west: {
49561        id: 'source-files',
49562        autoCreate:true,
49563        title:'Ext Source Files',
49564        autoScroll:true,
49565        fitToFrame:true
49566    },
49567    center : {
49568        el: cview,
49569        autoScroll:true,
49570        fitToFrame:true,
49571        toolbar: tb,
49572        resizeEl:'cbody'
49573    }
49574 });
49575 </code></pre>
49576      * @param {Object} regions An object containing ContentPanel configs by region name
49577      */
49578     batchAdd : function(regions){
49579         this.beginUpdate();
49580         for(var rname in regions){
49581             var lr = this.regions[rname];
49582             if(lr){
49583                 this.addTypedPanels(lr, regions[rname]);
49584             }
49585         }
49586         this.endUpdate();
49587     },
49588
49589     // private
49590     addTypedPanels : function(lr, ps){
49591         if(typeof ps == 'string'){
49592             lr.add(new Roo.ContentPanel(ps));
49593         }
49594         else if(ps instanceof Array){
49595             for(var i =0, len = ps.length; i < len; i++){
49596                 this.addTypedPanels(lr, ps[i]);
49597             }
49598         }
49599         else if(!ps.events){ // raw config?
49600             var el = ps.el;
49601             delete ps.el; // prevent conflict
49602             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49603         }
49604         else {  // panel object assumed!
49605             lr.add(ps);
49606         }
49607     },
49608     /**
49609      * Adds a xtype elements to the layout.
49610      * <pre><code>
49611
49612 layout.addxtype({
49613        xtype : 'ContentPanel',
49614        region: 'west',
49615        items: [ .... ]
49616    }
49617 );
49618
49619 layout.addxtype({
49620         xtype : 'NestedLayoutPanel',
49621         region: 'west',
49622         layout: {
49623            center: { },
49624            west: { }   
49625         },
49626         items : [ ... list of content panels or nested layout panels.. ]
49627    }
49628 );
49629 </code></pre>
49630      * @param {Object} cfg Xtype definition of item to add.
49631      */
49632     addxtype : function(cfg)
49633     {
49634         // basically accepts a pannel...
49635         // can accept a layout region..!?!?
49636         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49637         
49638         if (!cfg.xtype.match(/Panel$/)) {
49639             return false;
49640         }
49641         var ret = false;
49642         
49643         if (typeof(cfg.region) == 'undefined') {
49644             Roo.log("Failed to add Panel, region was not set");
49645             Roo.log(cfg);
49646             return false;
49647         }
49648         var region = cfg.region;
49649         delete cfg.region;
49650         
49651           
49652         var xitems = [];
49653         if (cfg.items) {
49654             xitems = cfg.items;
49655             delete cfg.items;
49656         }
49657         var nb = false;
49658         
49659         switch(cfg.xtype) 
49660         {
49661             case 'ContentPanel':  // ContentPanel (el, cfg)
49662             case 'ScrollPanel':  // ContentPanel (el, cfg)
49663             case 'ViewPanel': 
49664                 if(cfg.autoCreate) {
49665                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49666                 } else {
49667                     var el = this.el.createChild();
49668                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49669                 }
49670                 
49671                 this.add(region, ret);
49672                 break;
49673             
49674             
49675             case 'TreePanel': // our new panel!
49676                 cfg.el = this.el.createChild();
49677                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49678                 this.add(region, ret);
49679                 break;
49680             
49681             case 'NestedLayoutPanel': 
49682                 // create a new Layout (which is  a Border Layout...
49683                 var el = this.el.createChild();
49684                 var clayout = cfg.layout;
49685                 delete cfg.layout;
49686                 clayout.items   = clayout.items  || [];
49687                 // replace this exitems with the clayout ones..
49688                 xitems = clayout.items;
49689                  
49690                 
49691                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49692                     cfg.background = false;
49693                 }
49694                 var layout = new Roo.BorderLayout(el, clayout);
49695                 
49696                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49697                 //console.log('adding nested layout panel '  + cfg.toSource());
49698                 this.add(region, ret);
49699                 nb = {}; /// find first...
49700                 break;
49701                 
49702             case 'GridPanel': 
49703             
49704                 // needs grid and region
49705                 
49706                 //var el = this.getRegion(region).el.createChild();
49707                 var el = this.el.createChild();
49708                 // create the grid first...
49709                 
49710                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49711                 delete cfg.grid;
49712                 if (region == 'center' && this.active ) {
49713                     cfg.background = false;
49714                 }
49715                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49716                 
49717                 this.add(region, ret);
49718                 if (cfg.background) {
49719                     ret.on('activate', function(gp) {
49720                         if (!gp.grid.rendered) {
49721                             gp.grid.render();
49722                         }
49723                     });
49724                 } else {
49725                     grid.render();
49726                 }
49727                 break;
49728            
49729            
49730            
49731                 
49732                 
49733                 
49734             default:
49735                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49736                     
49737                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49738                     this.add(region, ret);
49739                 } else {
49740                 
49741                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49742                     return null;
49743                 }
49744                 
49745              // GridPanel (grid, cfg)
49746             
49747         }
49748         this.beginUpdate();
49749         // add children..
49750         var region = '';
49751         var abn = {};
49752         Roo.each(xitems, function(i)  {
49753             region = nb && i.region ? i.region : false;
49754             
49755             var add = ret.addxtype(i);
49756            
49757             if (region) {
49758                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49759                 if (!i.background) {
49760                     abn[region] = nb[region] ;
49761                 }
49762             }
49763             
49764         });
49765         this.endUpdate();
49766
49767         // make the last non-background panel active..
49768         //if (nb) { Roo.log(abn); }
49769         if (nb) {
49770             
49771             for(var r in abn) {
49772                 region = this.getRegion(r);
49773                 if (region) {
49774                     // tried using nb[r], but it does not work..
49775                      
49776                     region.showPanel(abn[r]);
49777                    
49778                 }
49779             }
49780         }
49781         return ret;
49782         
49783     }
49784 });
49785
49786 /**
49787  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49788  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49789  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49790  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49791  * <pre><code>
49792 // shorthand
49793 var CP = Roo.ContentPanel;
49794
49795 var layout = Roo.BorderLayout.create({
49796     north: {
49797         initialSize: 25,
49798         titlebar: false,
49799         panels: [new CP("north", "North")]
49800     },
49801     west: {
49802         split:true,
49803         initialSize: 200,
49804         minSize: 175,
49805         maxSize: 400,
49806         titlebar: true,
49807         collapsible: true,
49808         panels: [new CP("west", {title: "West"})]
49809     },
49810     east: {
49811         split:true,
49812         initialSize: 202,
49813         minSize: 175,
49814         maxSize: 400,
49815         titlebar: true,
49816         collapsible: true,
49817         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49818     },
49819     south: {
49820         split:true,
49821         initialSize: 100,
49822         minSize: 100,
49823         maxSize: 200,
49824         titlebar: true,
49825         collapsible: true,
49826         panels: [new CP("south", {title: "South", closable: true})]
49827     },
49828     center: {
49829         titlebar: true,
49830         autoScroll:true,
49831         resizeTabs: true,
49832         minTabWidth: 50,
49833         preferredTabWidth: 150,
49834         panels: [
49835             new CP("center1", {title: "Close Me", closable: true}),
49836             new CP("center2", {title: "Center Panel", closable: false})
49837         ]
49838     }
49839 }, document.body);
49840
49841 layout.getRegion("center").showPanel("center1");
49842 </code></pre>
49843  * @param config
49844  * @param targetEl
49845  */
49846 Roo.BorderLayout.create = function(config, targetEl){
49847     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49848     layout.beginUpdate();
49849     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49850     for(var j = 0, jlen = regions.length; j < jlen; j++){
49851         var lr = regions[j];
49852         if(layout.regions[lr] && config[lr].panels){
49853             var r = layout.regions[lr];
49854             var ps = config[lr].panels;
49855             layout.addTypedPanels(r, ps);
49856         }
49857     }
49858     layout.endUpdate();
49859     return layout;
49860 };
49861
49862 // private
49863 Roo.BorderLayout.RegionFactory = {
49864     // private
49865     validRegions : ["north","south","east","west","center"],
49866
49867     // private
49868     create : function(target, mgr, config){
49869         target = target.toLowerCase();
49870         if(config.lightweight || config.basic){
49871             return new Roo.BasicLayoutRegion(mgr, config, target);
49872         }
49873         switch(target){
49874             case "north":
49875                 return new Roo.NorthLayoutRegion(mgr, config);
49876             case "south":
49877                 return new Roo.SouthLayoutRegion(mgr, config);
49878             case "east":
49879                 return new Roo.EastLayoutRegion(mgr, config);
49880             case "west":
49881                 return new Roo.WestLayoutRegion(mgr, config);
49882             case "center":
49883                 return new Roo.CenterLayoutRegion(mgr, config);
49884         }
49885         throw 'Layout region "'+target+'" not supported.';
49886     }
49887 };/*
49888  * Based on:
49889  * Ext JS Library 1.1.1
49890  * Copyright(c) 2006-2007, Ext JS, LLC.
49891  *
49892  * Originally Released Under LGPL - original licence link has changed is not relivant.
49893  *
49894  * Fork - LGPL
49895  * <script type="text/javascript">
49896  */
49897  
49898 /**
49899  * @class Roo.BasicLayoutRegion
49900  * @extends Roo.util.Observable
49901  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49902  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49903  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49904  */
49905 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49906     this.mgr = mgr;
49907     this.position  = pos;
49908     this.events = {
49909         /**
49910          * @scope Roo.BasicLayoutRegion
49911          */
49912         
49913         /**
49914          * @event beforeremove
49915          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49916          * @param {Roo.LayoutRegion} this
49917          * @param {Roo.ContentPanel} panel The panel
49918          * @param {Object} e The cancel event object
49919          */
49920         "beforeremove" : true,
49921         /**
49922          * @event invalidated
49923          * Fires when the layout for this region is changed.
49924          * @param {Roo.LayoutRegion} this
49925          */
49926         "invalidated" : true,
49927         /**
49928          * @event visibilitychange
49929          * Fires when this region is shown or hidden 
49930          * @param {Roo.LayoutRegion} this
49931          * @param {Boolean} visibility true or false
49932          */
49933         "visibilitychange" : true,
49934         /**
49935          * @event paneladded
49936          * Fires when a panel is added. 
49937          * @param {Roo.LayoutRegion} this
49938          * @param {Roo.ContentPanel} panel The panel
49939          */
49940         "paneladded" : true,
49941         /**
49942          * @event panelremoved
49943          * Fires when a panel is removed. 
49944          * @param {Roo.LayoutRegion} this
49945          * @param {Roo.ContentPanel} panel The panel
49946          */
49947         "panelremoved" : true,
49948         /**
49949          * @event collapsed
49950          * Fires when this region is collapsed.
49951          * @param {Roo.LayoutRegion} this
49952          */
49953         "collapsed" : true,
49954         /**
49955          * @event expanded
49956          * Fires when this region is expanded.
49957          * @param {Roo.LayoutRegion} this
49958          */
49959         "expanded" : true,
49960         /**
49961          * @event slideshow
49962          * Fires when this region is slid into view.
49963          * @param {Roo.LayoutRegion} this
49964          */
49965         "slideshow" : true,
49966         /**
49967          * @event slidehide
49968          * Fires when this region slides out of view. 
49969          * @param {Roo.LayoutRegion} this
49970          */
49971         "slidehide" : true,
49972         /**
49973          * @event panelactivated
49974          * Fires when a panel is activated. 
49975          * @param {Roo.LayoutRegion} this
49976          * @param {Roo.ContentPanel} panel The activated panel
49977          */
49978         "panelactivated" : true,
49979         /**
49980          * @event resized
49981          * Fires when the user resizes this region. 
49982          * @param {Roo.LayoutRegion} this
49983          * @param {Number} newSize The new size (width for east/west, height for north/south)
49984          */
49985         "resized" : true
49986     };
49987     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49988     this.panels = new Roo.util.MixedCollection();
49989     this.panels.getKey = this.getPanelId.createDelegate(this);
49990     this.box = null;
49991     this.activePanel = null;
49992     // ensure listeners are added...
49993     
49994     if (config.listeners || config.events) {
49995         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49996             listeners : config.listeners || {},
49997             events : config.events || {}
49998         });
49999     }
50000     
50001     if(skipConfig !== true){
50002         this.applyConfig(config);
50003     }
50004 };
50005
50006 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
50007     getPanelId : function(p){
50008         return p.getId();
50009     },
50010     
50011     applyConfig : function(config){
50012         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50013         this.config = config;
50014         
50015     },
50016     
50017     /**
50018      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
50019      * the width, for horizontal (north, south) the height.
50020      * @param {Number} newSize The new width or height
50021      */
50022     resizeTo : function(newSize){
50023         var el = this.el ? this.el :
50024                  (this.activePanel ? this.activePanel.getEl() : null);
50025         if(el){
50026             switch(this.position){
50027                 case "east":
50028                 case "west":
50029                     el.setWidth(newSize);
50030                     this.fireEvent("resized", this, newSize);
50031                 break;
50032                 case "north":
50033                 case "south":
50034                     el.setHeight(newSize);
50035                     this.fireEvent("resized", this, newSize);
50036                 break;                
50037             }
50038         }
50039     },
50040     
50041     getBox : function(){
50042         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50043     },
50044     
50045     getMargins : function(){
50046         return this.margins;
50047     },
50048     
50049     updateBox : function(box){
50050         this.box = box;
50051         var el = this.activePanel.getEl();
50052         el.dom.style.left = box.x + "px";
50053         el.dom.style.top = box.y + "px";
50054         this.activePanel.setSize(box.width, box.height);
50055     },
50056     
50057     /**
50058      * Returns the container element for this region.
50059      * @return {Roo.Element}
50060      */
50061     getEl : function(){
50062         return this.activePanel;
50063     },
50064     
50065     /**
50066      * Returns true if this region is currently visible.
50067      * @return {Boolean}
50068      */
50069     isVisible : function(){
50070         return this.activePanel ? true : false;
50071     },
50072     
50073     setActivePanel : function(panel){
50074         panel = this.getPanel(panel);
50075         if(this.activePanel && this.activePanel != panel){
50076             this.activePanel.setActiveState(false);
50077             this.activePanel.getEl().setLeftTop(-10000,-10000);
50078         }
50079         this.activePanel = panel;
50080         panel.setActiveState(true);
50081         if(this.box){
50082             panel.setSize(this.box.width, this.box.height);
50083         }
50084         this.fireEvent("panelactivated", this, panel);
50085         this.fireEvent("invalidated");
50086     },
50087     
50088     /**
50089      * Show the specified panel.
50090      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50091      * @return {Roo.ContentPanel} The shown panel or null
50092      */
50093     showPanel : function(panel){
50094         if(panel = this.getPanel(panel)){
50095             this.setActivePanel(panel);
50096         }
50097         return panel;
50098     },
50099     
50100     /**
50101      * Get the active panel for this region.
50102      * @return {Roo.ContentPanel} The active panel or null
50103      */
50104     getActivePanel : function(){
50105         return this.activePanel;
50106     },
50107     
50108     /**
50109      * Add the passed ContentPanel(s)
50110      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50111      * @return {Roo.ContentPanel} The panel added (if only one was added)
50112      */
50113     add : function(panel){
50114         if(arguments.length > 1){
50115             for(var i = 0, len = arguments.length; i < len; i++) {
50116                 this.add(arguments[i]);
50117             }
50118             return null;
50119         }
50120         if(this.hasPanel(panel)){
50121             this.showPanel(panel);
50122             return panel;
50123         }
50124         var el = panel.getEl();
50125         if(el.dom.parentNode != this.mgr.el.dom){
50126             this.mgr.el.dom.appendChild(el.dom);
50127         }
50128         if(panel.setRegion){
50129             panel.setRegion(this);
50130         }
50131         this.panels.add(panel);
50132         el.setStyle("position", "absolute");
50133         if(!panel.background){
50134             this.setActivePanel(panel);
50135             if(this.config.initialSize && this.panels.getCount()==1){
50136                 this.resizeTo(this.config.initialSize);
50137             }
50138         }
50139         this.fireEvent("paneladded", this, panel);
50140         return panel;
50141     },
50142     
50143     /**
50144      * Returns true if the panel is in this region.
50145      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50146      * @return {Boolean}
50147      */
50148     hasPanel : function(panel){
50149         if(typeof panel == "object"){ // must be panel obj
50150             panel = panel.getId();
50151         }
50152         return this.getPanel(panel) ? true : false;
50153     },
50154     
50155     /**
50156      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50157      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50158      * @param {Boolean} preservePanel Overrides the config preservePanel option
50159      * @return {Roo.ContentPanel} The panel that was removed
50160      */
50161     remove : function(panel, preservePanel){
50162         panel = this.getPanel(panel);
50163         if(!panel){
50164             return null;
50165         }
50166         var e = {};
50167         this.fireEvent("beforeremove", this, panel, e);
50168         if(e.cancel === true){
50169             return null;
50170         }
50171         var panelId = panel.getId();
50172         this.panels.removeKey(panelId);
50173         return panel;
50174     },
50175     
50176     /**
50177      * Returns the panel specified or null if it's not in this region.
50178      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50179      * @return {Roo.ContentPanel}
50180      */
50181     getPanel : function(id){
50182         if(typeof id == "object"){ // must be panel obj
50183             return id;
50184         }
50185         return this.panels.get(id);
50186     },
50187     
50188     /**
50189      * Returns this regions position (north/south/east/west/center).
50190      * @return {String} 
50191      */
50192     getPosition: function(){
50193         return this.position;    
50194     }
50195 });/*
50196  * Based on:
50197  * Ext JS Library 1.1.1
50198  * Copyright(c) 2006-2007, Ext JS, LLC.
50199  *
50200  * Originally Released Under LGPL - original licence link has changed is not relivant.
50201  *
50202  * Fork - LGPL
50203  * <script type="text/javascript">
50204  */
50205  
50206 /**
50207  * @class Roo.LayoutRegion
50208  * @extends Roo.BasicLayoutRegion
50209  * This class represents a region in a layout manager.
50210  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50211  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50212  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50213  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50214  * @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})
50215  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50216  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50217  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50218  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50219  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50220  * @cfg {String}    title           The title for the region (overrides panel titles)
50221  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50222  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50223  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50224  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50225  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50226  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50227  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50228  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50229  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50230  * @cfg {Boolean}   showPin         True to show a pin button
50231  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50232  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50233  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50234  * @cfg {Number}    width           For East/West panels
50235  * @cfg {Number}    height          For North/South panels
50236  * @cfg {Boolean}   split           To show the splitter
50237  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50238  */
50239 Roo.LayoutRegion = function(mgr, config, pos){
50240     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50241     var dh = Roo.DomHelper;
50242     /** This region's container element 
50243     * @type Roo.Element */
50244     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50245     /** This region's title element 
50246     * @type Roo.Element */
50247
50248     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50249         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50250         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50251     ]}, true);
50252     this.titleEl.enableDisplayMode();
50253     /** This region's title text element 
50254     * @type HTMLElement */
50255     this.titleTextEl = this.titleEl.dom.firstChild;
50256     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50257     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50258     this.closeBtn.enableDisplayMode();
50259     this.closeBtn.on("click", this.closeClicked, this);
50260     this.closeBtn.hide();
50261
50262     this.createBody(config);
50263     this.visible = true;
50264     this.collapsed = false;
50265
50266     if(config.hideWhenEmpty){
50267         this.hide();
50268         this.on("paneladded", this.validateVisibility, this);
50269         this.on("panelremoved", this.validateVisibility, this);
50270     }
50271     this.applyConfig(config);
50272 };
50273
50274 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50275
50276     createBody : function(){
50277         /** This region's body element 
50278         * @type Roo.Element */
50279         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50280     },
50281
50282     applyConfig : function(c){
50283         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50284             var dh = Roo.DomHelper;
50285             if(c.titlebar !== false){
50286                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50287                 this.collapseBtn.on("click", this.collapse, this);
50288                 this.collapseBtn.enableDisplayMode();
50289
50290                 if(c.showPin === true || this.showPin){
50291                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50292                     this.stickBtn.enableDisplayMode();
50293                     this.stickBtn.on("click", this.expand, this);
50294                     this.stickBtn.hide();
50295                 }
50296             }
50297             /** This region's collapsed element
50298             * @type Roo.Element */
50299             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50300                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50301             ]}, true);
50302             if(c.floatable !== false){
50303                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50304                this.collapsedEl.on("click", this.collapseClick, this);
50305             }
50306
50307             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50308                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50309                    id: "message", unselectable: "on", style:{"float":"left"}});
50310                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50311              }
50312             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50313             this.expandBtn.on("click", this.expand, this);
50314         }
50315         if(this.collapseBtn){
50316             this.collapseBtn.setVisible(c.collapsible == true);
50317         }
50318         this.cmargins = c.cmargins || this.cmargins ||
50319                          (this.position == "west" || this.position == "east" ?
50320                              {top: 0, left: 2, right:2, bottom: 0} :
50321                              {top: 2, left: 0, right:0, bottom: 2});
50322         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50323         this.bottomTabs = c.tabPosition != "top";
50324         this.autoScroll = c.autoScroll || false;
50325         if(this.autoScroll){
50326             this.bodyEl.setStyle("overflow", "auto");
50327         }else{
50328             this.bodyEl.setStyle("overflow", "hidden");
50329         }
50330         //if(c.titlebar !== false){
50331             if((!c.titlebar && !c.title) || c.titlebar === false){
50332                 this.titleEl.hide();
50333             }else{
50334                 this.titleEl.show();
50335                 if(c.title){
50336                     this.titleTextEl.innerHTML = c.title;
50337                 }
50338             }
50339         //}
50340         this.duration = c.duration || .30;
50341         this.slideDuration = c.slideDuration || .45;
50342         this.config = c;
50343         if(c.collapsed){
50344             this.collapse(true);
50345         }
50346         if(c.hidden){
50347             this.hide();
50348         }
50349     },
50350     /**
50351      * Returns true if this region is currently visible.
50352      * @return {Boolean}
50353      */
50354     isVisible : function(){
50355         return this.visible;
50356     },
50357
50358     /**
50359      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50360      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50361      */
50362     setCollapsedTitle : function(title){
50363         title = title || "&#160;";
50364         if(this.collapsedTitleTextEl){
50365             this.collapsedTitleTextEl.innerHTML = title;
50366         }
50367     },
50368
50369     getBox : function(){
50370         var b;
50371         if(!this.collapsed){
50372             b = this.el.getBox(false, true);
50373         }else{
50374             b = this.collapsedEl.getBox(false, true);
50375         }
50376         return b;
50377     },
50378
50379     getMargins : function(){
50380         return this.collapsed ? this.cmargins : this.margins;
50381     },
50382
50383     highlight : function(){
50384         this.el.addClass("x-layout-panel-dragover");
50385     },
50386
50387     unhighlight : function(){
50388         this.el.removeClass("x-layout-panel-dragover");
50389     },
50390
50391     updateBox : function(box){
50392         this.box = box;
50393         if(!this.collapsed){
50394             this.el.dom.style.left = box.x + "px";
50395             this.el.dom.style.top = box.y + "px";
50396             this.updateBody(box.width, box.height);
50397         }else{
50398             this.collapsedEl.dom.style.left = box.x + "px";
50399             this.collapsedEl.dom.style.top = box.y + "px";
50400             this.collapsedEl.setSize(box.width, box.height);
50401         }
50402         if(this.tabs){
50403             this.tabs.autoSizeTabs();
50404         }
50405     },
50406
50407     updateBody : function(w, h){
50408         if(w !== null){
50409             this.el.setWidth(w);
50410             w -= this.el.getBorderWidth("rl");
50411             if(this.config.adjustments){
50412                 w += this.config.adjustments[0];
50413             }
50414         }
50415         if(h !== null){
50416             this.el.setHeight(h);
50417             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50418             h -= this.el.getBorderWidth("tb");
50419             if(this.config.adjustments){
50420                 h += this.config.adjustments[1];
50421             }
50422             this.bodyEl.setHeight(h);
50423             if(this.tabs){
50424                 h = this.tabs.syncHeight(h);
50425             }
50426         }
50427         if(this.panelSize){
50428             w = w !== null ? w : this.panelSize.width;
50429             h = h !== null ? h : this.panelSize.height;
50430         }
50431         if(this.activePanel){
50432             var el = this.activePanel.getEl();
50433             w = w !== null ? w : el.getWidth();
50434             h = h !== null ? h : el.getHeight();
50435             this.panelSize = {width: w, height: h};
50436             this.activePanel.setSize(w, h);
50437         }
50438         if(Roo.isIE && this.tabs){
50439             this.tabs.el.repaint();
50440         }
50441     },
50442
50443     /**
50444      * Returns the container element for this region.
50445      * @return {Roo.Element}
50446      */
50447     getEl : function(){
50448         return this.el;
50449     },
50450
50451     /**
50452      * Hides this region.
50453      */
50454     hide : function(){
50455         if(!this.collapsed){
50456             this.el.dom.style.left = "-2000px";
50457             this.el.hide();
50458         }else{
50459             this.collapsedEl.dom.style.left = "-2000px";
50460             this.collapsedEl.hide();
50461         }
50462         this.visible = false;
50463         this.fireEvent("visibilitychange", this, false);
50464     },
50465
50466     /**
50467      * Shows this region if it was previously hidden.
50468      */
50469     show : function(){
50470         if(!this.collapsed){
50471             this.el.show();
50472         }else{
50473             this.collapsedEl.show();
50474         }
50475         this.visible = true;
50476         this.fireEvent("visibilitychange", this, true);
50477     },
50478
50479     closeClicked : function(){
50480         if(this.activePanel){
50481             this.remove(this.activePanel);
50482         }
50483     },
50484
50485     collapseClick : function(e){
50486         if(this.isSlid){
50487            e.stopPropagation();
50488            this.slideIn();
50489         }else{
50490            e.stopPropagation();
50491            this.slideOut();
50492         }
50493     },
50494
50495     /**
50496      * Collapses this region.
50497      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50498      */
50499     collapse : function(skipAnim){
50500         if(this.collapsed) return;
50501         this.collapsed = true;
50502         if(this.split){
50503             this.split.el.hide();
50504         }
50505         if(this.config.animate && skipAnim !== true){
50506             this.fireEvent("invalidated", this);
50507             this.animateCollapse();
50508         }else{
50509             this.el.setLocation(-20000,-20000);
50510             this.el.hide();
50511             this.collapsedEl.show();
50512             this.fireEvent("collapsed", this);
50513             this.fireEvent("invalidated", this);
50514         }
50515     },
50516
50517     animateCollapse : function(){
50518         // overridden
50519     },
50520
50521     /**
50522      * Expands this region if it was previously collapsed.
50523      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50524      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50525      */
50526     expand : function(e, skipAnim){
50527         if(e) e.stopPropagation();
50528         if(!this.collapsed || this.el.hasActiveFx()) return;
50529         if(this.isSlid){
50530             this.afterSlideIn();
50531             skipAnim = true;
50532         }
50533         this.collapsed = false;
50534         if(this.config.animate && skipAnim !== true){
50535             this.animateExpand();
50536         }else{
50537             this.el.show();
50538             if(this.split){
50539                 this.split.el.show();
50540             }
50541             this.collapsedEl.setLocation(-2000,-2000);
50542             this.collapsedEl.hide();
50543             this.fireEvent("invalidated", this);
50544             this.fireEvent("expanded", this);
50545         }
50546     },
50547
50548     animateExpand : function(){
50549         // overridden
50550     },
50551
50552     initTabs : function()
50553     {
50554         this.bodyEl.setStyle("overflow", "hidden");
50555         var ts = new Roo.TabPanel(
50556                 this.bodyEl.dom,
50557                 {
50558                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50559                     disableTooltips: this.config.disableTabTips,
50560                     toolbar : this.config.toolbar
50561                 }
50562         );
50563         if(this.config.hideTabs){
50564             ts.stripWrap.setDisplayed(false);
50565         }
50566         this.tabs = ts;
50567         ts.resizeTabs = this.config.resizeTabs === true;
50568         ts.minTabWidth = this.config.minTabWidth || 40;
50569         ts.maxTabWidth = this.config.maxTabWidth || 250;
50570         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50571         ts.monitorResize = false;
50572         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50573         ts.bodyEl.addClass('x-layout-tabs-body');
50574         this.panels.each(this.initPanelAsTab, this);
50575     },
50576
50577     initPanelAsTab : function(panel){
50578         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50579                     this.config.closeOnTab && panel.isClosable());
50580         if(panel.tabTip !== undefined){
50581             ti.setTooltip(panel.tabTip);
50582         }
50583         ti.on("activate", function(){
50584               this.setActivePanel(panel);
50585         }, this);
50586         if(this.config.closeOnTab){
50587             ti.on("beforeclose", function(t, e){
50588                 e.cancel = true;
50589                 this.remove(panel);
50590             }, this);
50591         }
50592         return ti;
50593     },
50594
50595     updatePanelTitle : function(panel, title){
50596         if(this.activePanel == panel){
50597             this.updateTitle(title);
50598         }
50599         if(this.tabs){
50600             var ti = this.tabs.getTab(panel.getEl().id);
50601             ti.setText(title);
50602             if(panel.tabTip !== undefined){
50603                 ti.setTooltip(panel.tabTip);
50604             }
50605         }
50606     },
50607
50608     updateTitle : function(title){
50609         if(this.titleTextEl && !this.config.title){
50610             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50611         }
50612     },
50613
50614     setActivePanel : function(panel){
50615         panel = this.getPanel(panel);
50616         if(this.activePanel && this.activePanel != panel){
50617             this.activePanel.setActiveState(false);
50618         }
50619         this.activePanel = panel;
50620         panel.setActiveState(true);
50621         if(this.panelSize){
50622             panel.setSize(this.panelSize.width, this.panelSize.height);
50623         }
50624         if(this.closeBtn){
50625             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50626         }
50627         this.updateTitle(panel.getTitle());
50628         if(this.tabs){
50629             this.fireEvent("invalidated", this);
50630         }
50631         this.fireEvent("panelactivated", this, panel);
50632     },
50633
50634     /**
50635      * Shows the specified panel.
50636      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50637      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50638      */
50639     showPanel : function(panel)
50640     {
50641         panel = this.getPanel(panel);
50642         if(panel){
50643             if(this.tabs){
50644                 var tab = this.tabs.getTab(panel.getEl().id);
50645                 if(tab.isHidden()){
50646                     this.tabs.unhideTab(tab.id);
50647                 }
50648                 tab.activate();
50649             }else{
50650                 this.setActivePanel(panel);
50651             }
50652         }
50653         return panel;
50654     },
50655
50656     /**
50657      * Get the active panel for this region.
50658      * @return {Roo.ContentPanel} The active panel or null
50659      */
50660     getActivePanel : function(){
50661         return this.activePanel;
50662     },
50663
50664     validateVisibility : function(){
50665         if(this.panels.getCount() < 1){
50666             this.updateTitle("&#160;");
50667             this.closeBtn.hide();
50668             this.hide();
50669         }else{
50670             if(!this.isVisible()){
50671                 this.show();
50672             }
50673         }
50674     },
50675
50676     /**
50677      * Adds the passed ContentPanel(s) to this region.
50678      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50679      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50680      */
50681     add : function(panel){
50682         if(arguments.length > 1){
50683             for(var i = 0, len = arguments.length; i < len; i++) {
50684                 this.add(arguments[i]);
50685             }
50686             return null;
50687         }
50688         if(this.hasPanel(panel)){
50689             this.showPanel(panel);
50690             return panel;
50691         }
50692         panel.setRegion(this);
50693         this.panels.add(panel);
50694         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50695             this.bodyEl.dom.appendChild(panel.getEl().dom);
50696             if(panel.background !== true){
50697                 this.setActivePanel(panel);
50698             }
50699             this.fireEvent("paneladded", this, panel);
50700             return panel;
50701         }
50702         if(!this.tabs){
50703             this.initTabs();
50704         }else{
50705             this.initPanelAsTab(panel);
50706         }
50707         if(panel.background !== true){
50708             this.tabs.activate(panel.getEl().id);
50709         }
50710         this.fireEvent("paneladded", this, panel);
50711         return panel;
50712     },
50713
50714     /**
50715      * Hides the tab for the specified panel.
50716      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50717      */
50718     hidePanel : function(panel){
50719         if(this.tabs && (panel = this.getPanel(panel))){
50720             this.tabs.hideTab(panel.getEl().id);
50721         }
50722     },
50723
50724     /**
50725      * Unhides the tab for a previously hidden panel.
50726      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50727      */
50728     unhidePanel : function(panel){
50729         if(this.tabs && (panel = this.getPanel(panel))){
50730             this.tabs.unhideTab(panel.getEl().id);
50731         }
50732     },
50733
50734     clearPanels : function(){
50735         while(this.panels.getCount() > 0){
50736              this.remove(this.panels.first());
50737         }
50738     },
50739
50740     /**
50741      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50742      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50743      * @param {Boolean} preservePanel Overrides the config preservePanel option
50744      * @return {Roo.ContentPanel} The panel that was removed
50745      */
50746     remove : function(panel, preservePanel){
50747         panel = this.getPanel(panel);
50748         if(!panel){
50749             return null;
50750         }
50751         var e = {};
50752         this.fireEvent("beforeremove", this, panel, e);
50753         if(e.cancel === true){
50754             return null;
50755         }
50756         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50757         var panelId = panel.getId();
50758         this.panels.removeKey(panelId);
50759         if(preservePanel){
50760             document.body.appendChild(panel.getEl().dom);
50761         }
50762         if(this.tabs){
50763             this.tabs.removeTab(panel.getEl().id);
50764         }else if (!preservePanel){
50765             this.bodyEl.dom.removeChild(panel.getEl().dom);
50766         }
50767         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50768             var p = this.panels.first();
50769             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50770             tempEl.appendChild(p.getEl().dom);
50771             this.bodyEl.update("");
50772             this.bodyEl.dom.appendChild(p.getEl().dom);
50773             tempEl = null;
50774             this.updateTitle(p.getTitle());
50775             this.tabs = null;
50776             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50777             this.setActivePanel(p);
50778         }
50779         panel.setRegion(null);
50780         if(this.activePanel == panel){
50781             this.activePanel = null;
50782         }
50783         if(this.config.autoDestroy !== false && preservePanel !== true){
50784             try{panel.destroy();}catch(e){}
50785         }
50786         this.fireEvent("panelremoved", this, panel);
50787         return panel;
50788     },
50789
50790     /**
50791      * Returns the TabPanel component used by this region
50792      * @return {Roo.TabPanel}
50793      */
50794     getTabs : function(){
50795         return this.tabs;
50796     },
50797
50798     createTool : function(parentEl, className){
50799         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50800             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50801         btn.addClassOnOver("x-layout-tools-button-over");
50802         return btn;
50803     }
50804 });/*
50805  * Based on:
50806  * Ext JS Library 1.1.1
50807  * Copyright(c) 2006-2007, Ext JS, LLC.
50808  *
50809  * Originally Released Under LGPL - original licence link has changed is not relivant.
50810  *
50811  * Fork - LGPL
50812  * <script type="text/javascript">
50813  */
50814  
50815
50816
50817 /**
50818  * @class Roo.SplitLayoutRegion
50819  * @extends Roo.LayoutRegion
50820  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50821  */
50822 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50823     this.cursor = cursor;
50824     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50825 };
50826
50827 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50828     splitTip : "Drag to resize.",
50829     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50830     useSplitTips : false,
50831
50832     applyConfig : function(config){
50833         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50834         if(config.split){
50835             if(!this.split){
50836                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50837                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50838                 /** The SplitBar for this region 
50839                 * @type Roo.SplitBar */
50840                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50841                 this.split.on("moved", this.onSplitMove, this);
50842                 this.split.useShim = config.useShim === true;
50843                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50844                 if(this.useSplitTips){
50845                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50846                 }
50847                 if(config.collapsible){
50848                     this.split.el.on("dblclick", this.collapse,  this);
50849                 }
50850             }
50851             if(typeof config.minSize != "undefined"){
50852                 this.split.minSize = config.minSize;
50853             }
50854             if(typeof config.maxSize != "undefined"){
50855                 this.split.maxSize = config.maxSize;
50856             }
50857             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50858                 this.hideSplitter();
50859             }
50860         }
50861     },
50862
50863     getHMaxSize : function(){
50864          var cmax = this.config.maxSize || 10000;
50865          var center = this.mgr.getRegion("center");
50866          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50867     },
50868
50869     getVMaxSize : function(){
50870          var cmax = this.config.maxSize || 10000;
50871          var center = this.mgr.getRegion("center");
50872          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50873     },
50874
50875     onSplitMove : function(split, newSize){
50876         this.fireEvent("resized", this, newSize);
50877     },
50878     
50879     /** 
50880      * Returns the {@link Roo.SplitBar} for this region.
50881      * @return {Roo.SplitBar}
50882      */
50883     getSplitBar : function(){
50884         return this.split;
50885     },
50886     
50887     hide : function(){
50888         this.hideSplitter();
50889         Roo.SplitLayoutRegion.superclass.hide.call(this);
50890     },
50891
50892     hideSplitter : function(){
50893         if(this.split){
50894             this.split.el.setLocation(-2000,-2000);
50895             this.split.el.hide();
50896         }
50897     },
50898
50899     show : function(){
50900         if(this.split){
50901             this.split.el.show();
50902         }
50903         Roo.SplitLayoutRegion.superclass.show.call(this);
50904     },
50905     
50906     beforeSlide: function(){
50907         if(Roo.isGecko){// firefox overflow auto bug workaround
50908             this.bodyEl.clip();
50909             if(this.tabs) this.tabs.bodyEl.clip();
50910             if(this.activePanel){
50911                 this.activePanel.getEl().clip();
50912                 
50913                 if(this.activePanel.beforeSlide){
50914                     this.activePanel.beforeSlide();
50915                 }
50916             }
50917         }
50918     },
50919     
50920     afterSlide : function(){
50921         if(Roo.isGecko){// firefox overflow auto bug workaround
50922             this.bodyEl.unclip();
50923             if(this.tabs) this.tabs.bodyEl.unclip();
50924             if(this.activePanel){
50925                 this.activePanel.getEl().unclip();
50926                 if(this.activePanel.afterSlide){
50927                     this.activePanel.afterSlide();
50928                 }
50929             }
50930         }
50931     },
50932
50933     initAutoHide : function(){
50934         if(this.autoHide !== false){
50935             if(!this.autoHideHd){
50936                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50937                 this.autoHideHd = {
50938                     "mouseout": function(e){
50939                         if(!e.within(this.el, true)){
50940                             st.delay(500);
50941                         }
50942                     },
50943                     "mouseover" : function(e){
50944                         st.cancel();
50945                     },
50946                     scope : this
50947                 };
50948             }
50949             this.el.on(this.autoHideHd);
50950         }
50951     },
50952
50953     clearAutoHide : function(){
50954         if(this.autoHide !== false){
50955             this.el.un("mouseout", this.autoHideHd.mouseout);
50956             this.el.un("mouseover", this.autoHideHd.mouseover);
50957         }
50958     },
50959
50960     clearMonitor : function(){
50961         Roo.get(document).un("click", this.slideInIf, this);
50962     },
50963
50964     // these names are backwards but not changed for compat
50965     slideOut : function(){
50966         if(this.isSlid || this.el.hasActiveFx()){
50967             return;
50968         }
50969         this.isSlid = true;
50970         if(this.collapseBtn){
50971             this.collapseBtn.hide();
50972         }
50973         this.closeBtnState = this.closeBtn.getStyle('display');
50974         this.closeBtn.hide();
50975         if(this.stickBtn){
50976             this.stickBtn.show();
50977         }
50978         this.el.show();
50979         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50980         this.beforeSlide();
50981         this.el.setStyle("z-index", 10001);
50982         this.el.slideIn(this.getSlideAnchor(), {
50983             callback: function(){
50984                 this.afterSlide();
50985                 this.initAutoHide();
50986                 Roo.get(document).on("click", this.slideInIf, this);
50987                 this.fireEvent("slideshow", this);
50988             },
50989             scope: this,
50990             block: true
50991         });
50992     },
50993
50994     afterSlideIn : function(){
50995         this.clearAutoHide();
50996         this.isSlid = false;
50997         this.clearMonitor();
50998         this.el.setStyle("z-index", "");
50999         if(this.collapseBtn){
51000             this.collapseBtn.show();
51001         }
51002         this.closeBtn.setStyle('display', this.closeBtnState);
51003         if(this.stickBtn){
51004             this.stickBtn.hide();
51005         }
51006         this.fireEvent("slidehide", this);
51007     },
51008
51009     slideIn : function(cb){
51010         if(!this.isSlid || this.el.hasActiveFx()){
51011             Roo.callback(cb);
51012             return;
51013         }
51014         this.isSlid = false;
51015         this.beforeSlide();
51016         this.el.slideOut(this.getSlideAnchor(), {
51017             callback: function(){
51018                 this.el.setLeftTop(-10000, -10000);
51019                 this.afterSlide();
51020                 this.afterSlideIn();
51021                 Roo.callback(cb);
51022             },
51023             scope: this,
51024             block: true
51025         });
51026     },
51027     
51028     slideInIf : function(e){
51029         if(!e.within(this.el)){
51030             this.slideIn();
51031         }
51032     },
51033
51034     animateCollapse : function(){
51035         this.beforeSlide();
51036         this.el.setStyle("z-index", 20000);
51037         var anchor = this.getSlideAnchor();
51038         this.el.slideOut(anchor, {
51039             callback : function(){
51040                 this.el.setStyle("z-index", "");
51041                 this.collapsedEl.slideIn(anchor, {duration:.3});
51042                 this.afterSlide();
51043                 this.el.setLocation(-10000,-10000);
51044                 this.el.hide();
51045                 this.fireEvent("collapsed", this);
51046             },
51047             scope: this,
51048             block: true
51049         });
51050     },
51051
51052     animateExpand : function(){
51053         this.beforeSlide();
51054         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51055         this.el.setStyle("z-index", 20000);
51056         this.collapsedEl.hide({
51057             duration:.1
51058         });
51059         this.el.slideIn(this.getSlideAnchor(), {
51060             callback : function(){
51061                 this.el.setStyle("z-index", "");
51062                 this.afterSlide();
51063                 if(this.split){
51064                     this.split.el.show();
51065                 }
51066                 this.fireEvent("invalidated", this);
51067                 this.fireEvent("expanded", this);
51068             },
51069             scope: this,
51070             block: true
51071         });
51072     },
51073
51074     anchors : {
51075         "west" : "left",
51076         "east" : "right",
51077         "north" : "top",
51078         "south" : "bottom"
51079     },
51080
51081     sanchors : {
51082         "west" : "l",
51083         "east" : "r",
51084         "north" : "t",
51085         "south" : "b"
51086     },
51087
51088     canchors : {
51089         "west" : "tl-tr",
51090         "east" : "tr-tl",
51091         "north" : "tl-bl",
51092         "south" : "bl-tl"
51093     },
51094
51095     getAnchor : function(){
51096         return this.anchors[this.position];
51097     },
51098
51099     getCollapseAnchor : function(){
51100         return this.canchors[this.position];
51101     },
51102
51103     getSlideAnchor : function(){
51104         return this.sanchors[this.position];
51105     },
51106
51107     getAlignAdj : function(){
51108         var cm = this.cmargins;
51109         switch(this.position){
51110             case "west":
51111                 return [0, 0];
51112             break;
51113             case "east":
51114                 return [0, 0];
51115             break;
51116             case "north":
51117                 return [0, 0];
51118             break;
51119             case "south":
51120                 return [0, 0];
51121             break;
51122         }
51123     },
51124
51125     getExpandAdj : function(){
51126         var c = this.collapsedEl, cm = this.cmargins;
51127         switch(this.position){
51128             case "west":
51129                 return [-(cm.right+c.getWidth()+cm.left), 0];
51130             break;
51131             case "east":
51132                 return [cm.right+c.getWidth()+cm.left, 0];
51133             break;
51134             case "north":
51135                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51136             break;
51137             case "south":
51138                 return [0, cm.top+cm.bottom+c.getHeight()];
51139             break;
51140         }
51141     }
51142 });/*
51143  * Based on:
51144  * Ext JS Library 1.1.1
51145  * Copyright(c) 2006-2007, Ext JS, LLC.
51146  *
51147  * Originally Released Under LGPL - original licence link has changed is not relivant.
51148  *
51149  * Fork - LGPL
51150  * <script type="text/javascript">
51151  */
51152 /*
51153  * These classes are private internal classes
51154  */
51155 Roo.CenterLayoutRegion = function(mgr, config){
51156     Roo.LayoutRegion.call(this, mgr, config, "center");
51157     this.visible = true;
51158     this.minWidth = config.minWidth || 20;
51159     this.minHeight = config.minHeight || 20;
51160 };
51161
51162 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51163     hide : function(){
51164         // center panel can't be hidden
51165     },
51166     
51167     show : function(){
51168         // center panel can't be hidden
51169     },
51170     
51171     getMinWidth: function(){
51172         return this.minWidth;
51173     },
51174     
51175     getMinHeight: function(){
51176         return this.minHeight;
51177     }
51178 });
51179
51180
51181 Roo.NorthLayoutRegion = function(mgr, config){
51182     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51183     if(this.split){
51184         this.split.placement = Roo.SplitBar.TOP;
51185         this.split.orientation = Roo.SplitBar.VERTICAL;
51186         this.split.el.addClass("x-layout-split-v");
51187     }
51188     var size = config.initialSize || config.height;
51189     if(typeof size != "undefined"){
51190         this.el.setHeight(size);
51191     }
51192 };
51193 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51194     orientation: Roo.SplitBar.VERTICAL,
51195     getBox : function(){
51196         if(this.collapsed){
51197             return this.collapsedEl.getBox();
51198         }
51199         var box = this.el.getBox();
51200         if(this.split){
51201             box.height += this.split.el.getHeight();
51202         }
51203         return box;
51204     },
51205     
51206     updateBox : function(box){
51207         if(this.split && !this.collapsed){
51208             box.height -= this.split.el.getHeight();
51209             this.split.el.setLeft(box.x);
51210             this.split.el.setTop(box.y+box.height);
51211             this.split.el.setWidth(box.width);
51212         }
51213         if(this.collapsed){
51214             this.updateBody(box.width, null);
51215         }
51216         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51217     }
51218 });
51219
51220 Roo.SouthLayoutRegion = function(mgr, config){
51221     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51222     if(this.split){
51223         this.split.placement = Roo.SplitBar.BOTTOM;
51224         this.split.orientation = Roo.SplitBar.VERTICAL;
51225         this.split.el.addClass("x-layout-split-v");
51226     }
51227     var size = config.initialSize || config.height;
51228     if(typeof size != "undefined"){
51229         this.el.setHeight(size);
51230     }
51231 };
51232 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51233     orientation: Roo.SplitBar.VERTICAL,
51234     getBox : function(){
51235         if(this.collapsed){
51236             return this.collapsedEl.getBox();
51237         }
51238         var box = this.el.getBox();
51239         if(this.split){
51240             var sh = this.split.el.getHeight();
51241             box.height += sh;
51242             box.y -= sh;
51243         }
51244         return box;
51245     },
51246     
51247     updateBox : function(box){
51248         if(this.split && !this.collapsed){
51249             var sh = this.split.el.getHeight();
51250             box.height -= sh;
51251             box.y += sh;
51252             this.split.el.setLeft(box.x);
51253             this.split.el.setTop(box.y-sh);
51254             this.split.el.setWidth(box.width);
51255         }
51256         if(this.collapsed){
51257             this.updateBody(box.width, null);
51258         }
51259         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51260     }
51261 });
51262
51263 Roo.EastLayoutRegion = function(mgr, config){
51264     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51265     if(this.split){
51266         this.split.placement = Roo.SplitBar.RIGHT;
51267         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51268         this.split.el.addClass("x-layout-split-h");
51269     }
51270     var size = config.initialSize || config.width;
51271     if(typeof size != "undefined"){
51272         this.el.setWidth(size);
51273     }
51274 };
51275 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51276     orientation: Roo.SplitBar.HORIZONTAL,
51277     getBox : function(){
51278         if(this.collapsed){
51279             return this.collapsedEl.getBox();
51280         }
51281         var box = this.el.getBox();
51282         if(this.split){
51283             var sw = this.split.el.getWidth();
51284             box.width += sw;
51285             box.x -= sw;
51286         }
51287         return box;
51288     },
51289
51290     updateBox : function(box){
51291         if(this.split && !this.collapsed){
51292             var sw = this.split.el.getWidth();
51293             box.width -= sw;
51294             this.split.el.setLeft(box.x);
51295             this.split.el.setTop(box.y);
51296             this.split.el.setHeight(box.height);
51297             box.x += sw;
51298         }
51299         if(this.collapsed){
51300             this.updateBody(null, box.height);
51301         }
51302         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51303     }
51304 });
51305
51306 Roo.WestLayoutRegion = function(mgr, config){
51307     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51308     if(this.split){
51309         this.split.placement = Roo.SplitBar.LEFT;
51310         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51311         this.split.el.addClass("x-layout-split-h");
51312     }
51313     var size = config.initialSize || config.width;
51314     if(typeof size != "undefined"){
51315         this.el.setWidth(size);
51316     }
51317 };
51318 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51319     orientation: Roo.SplitBar.HORIZONTAL,
51320     getBox : function(){
51321         if(this.collapsed){
51322             return this.collapsedEl.getBox();
51323         }
51324         var box = this.el.getBox();
51325         if(this.split){
51326             box.width += this.split.el.getWidth();
51327         }
51328         return box;
51329     },
51330     
51331     updateBox : function(box){
51332         if(this.split && !this.collapsed){
51333             var sw = this.split.el.getWidth();
51334             box.width -= sw;
51335             this.split.el.setLeft(box.x+box.width);
51336             this.split.el.setTop(box.y);
51337             this.split.el.setHeight(box.height);
51338         }
51339         if(this.collapsed){
51340             this.updateBody(null, box.height);
51341         }
51342         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51343     }
51344 });
51345 /*
51346  * Based on:
51347  * Ext JS Library 1.1.1
51348  * Copyright(c) 2006-2007, Ext JS, LLC.
51349  *
51350  * Originally Released Under LGPL - original licence link has changed is not relivant.
51351  *
51352  * Fork - LGPL
51353  * <script type="text/javascript">
51354  */
51355  
51356  
51357 /*
51358  * Private internal class for reading and applying state
51359  */
51360 Roo.LayoutStateManager = function(layout){
51361      // default empty state
51362      this.state = {
51363         north: {},
51364         south: {},
51365         east: {},
51366         west: {}       
51367     };
51368 };
51369
51370 Roo.LayoutStateManager.prototype = {
51371     init : function(layout, provider){
51372         this.provider = provider;
51373         var state = provider.get(layout.id+"-layout-state");
51374         if(state){
51375             var wasUpdating = layout.isUpdating();
51376             if(!wasUpdating){
51377                 layout.beginUpdate();
51378             }
51379             for(var key in state){
51380                 if(typeof state[key] != "function"){
51381                     var rstate = state[key];
51382                     var r = layout.getRegion(key);
51383                     if(r && rstate){
51384                         if(rstate.size){
51385                             r.resizeTo(rstate.size);
51386                         }
51387                         if(rstate.collapsed == true){
51388                             r.collapse(true);
51389                         }else{
51390                             r.expand(null, true);
51391                         }
51392                     }
51393                 }
51394             }
51395             if(!wasUpdating){
51396                 layout.endUpdate();
51397             }
51398             this.state = state; 
51399         }
51400         this.layout = layout;
51401         layout.on("regionresized", this.onRegionResized, this);
51402         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51403         layout.on("regionexpanded", this.onRegionExpanded, this);
51404     },
51405     
51406     storeState : function(){
51407         this.provider.set(this.layout.id+"-layout-state", this.state);
51408     },
51409     
51410     onRegionResized : function(region, newSize){
51411         this.state[region.getPosition()].size = newSize;
51412         this.storeState();
51413     },
51414     
51415     onRegionCollapsed : function(region){
51416         this.state[region.getPosition()].collapsed = true;
51417         this.storeState();
51418     },
51419     
51420     onRegionExpanded : function(region){
51421         this.state[region.getPosition()].collapsed = false;
51422         this.storeState();
51423     }
51424 };/*
51425  * Based on:
51426  * Ext JS Library 1.1.1
51427  * Copyright(c) 2006-2007, Ext JS, LLC.
51428  *
51429  * Originally Released Under LGPL - original licence link has changed is not relivant.
51430  *
51431  * Fork - LGPL
51432  * <script type="text/javascript">
51433  */
51434 /**
51435  * @class Roo.ContentPanel
51436  * @extends Roo.util.Observable
51437  * A basic ContentPanel element.
51438  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51439  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51440  * @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
51441  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51442  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51443  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51444  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51445  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51446  * @cfg {String} title          The title for this panel
51447  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51448  * @cfg {String} url            Calls {@link #setUrl} with this value
51449  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51450  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51451  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51452  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51453
51454  * @constructor
51455  * Create a new ContentPanel.
51456  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51457  * @param {String/Object} config A string to set only the title or a config object
51458  * @param {String} content (optional) Set the HTML content for this panel
51459  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51460  */
51461 Roo.ContentPanel = function(el, config, content){
51462     
51463      
51464     /*
51465     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51466         config = el;
51467         el = Roo.id();
51468     }
51469     if (config && config.parentLayout) { 
51470         el = config.parentLayout.el.createChild(); 
51471     }
51472     */
51473     if(el.autoCreate){ // xtype is available if this is called from factory
51474         config = el;
51475         el = Roo.id();
51476     }
51477     this.el = Roo.get(el);
51478     if(!this.el && config && config.autoCreate){
51479         if(typeof config.autoCreate == "object"){
51480             if(!config.autoCreate.id){
51481                 config.autoCreate.id = config.id||el;
51482             }
51483             this.el = Roo.DomHelper.append(document.body,
51484                         config.autoCreate, true);
51485         }else{
51486             this.el = Roo.DomHelper.append(document.body,
51487                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51488         }
51489     }
51490     this.closable = false;
51491     this.loaded = false;
51492     this.active = false;
51493     if(typeof config == "string"){
51494         this.title = config;
51495     }else{
51496         Roo.apply(this, config);
51497     }
51498     
51499     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51500         this.wrapEl = this.el.wrap();
51501         this.toolbar.container = this.el.insertSibling(false, 'before');
51502         this.toolbar = new Roo.Toolbar(this.toolbar);
51503     }
51504     
51505     // xtype created footer. - not sure if will work as we normally have to render first..
51506     if (this.footer && !this.footer.el && this.footer.xtype) {
51507         if (!this.wrapEl) {
51508             this.wrapEl = this.el.wrap();
51509         }
51510     
51511         this.footer.container = this.wrapEl.createChild();
51512          
51513         this.footer = Roo.factory(this.footer, Roo);
51514         
51515     }
51516     
51517     if(this.resizeEl){
51518         this.resizeEl = Roo.get(this.resizeEl, true);
51519     }else{
51520         this.resizeEl = this.el;
51521     }
51522     // handle view.xtype
51523     
51524  
51525     
51526     
51527     this.addEvents({
51528         /**
51529          * @event activate
51530          * Fires when this panel is activated. 
51531          * @param {Roo.ContentPanel} this
51532          */
51533         "activate" : true,
51534         /**
51535          * @event deactivate
51536          * Fires when this panel is activated. 
51537          * @param {Roo.ContentPanel} this
51538          */
51539         "deactivate" : true,
51540
51541         /**
51542          * @event resize
51543          * Fires when this panel is resized if fitToFrame is true.
51544          * @param {Roo.ContentPanel} this
51545          * @param {Number} width The width after any component adjustments
51546          * @param {Number} height The height after any component adjustments
51547          */
51548         "resize" : true,
51549         
51550          /**
51551          * @event render
51552          * Fires when this tab is created
51553          * @param {Roo.ContentPanel} this
51554          */
51555         "render" : true
51556         
51557         
51558         
51559     });
51560     
51561
51562     
51563     
51564     if(this.autoScroll){
51565         this.resizeEl.setStyle("overflow", "auto");
51566     } else {
51567         // fix randome scrolling
51568         this.el.on('scroll', function() {
51569             Roo.log('fix random scolling');
51570             this.scrollTo('top',0); 
51571         });
51572     }
51573     content = content || this.content;
51574     if(content){
51575         this.setContent(content);
51576     }
51577     if(config && config.url){
51578         this.setUrl(this.url, this.params, this.loadOnce);
51579     }
51580     
51581     
51582     
51583     Roo.ContentPanel.superclass.constructor.call(this);
51584     
51585     if (this.view && typeof(this.view.xtype) != 'undefined') {
51586         this.view.el = this.el.appendChild(document.createElement("div"));
51587         this.view = Roo.factory(this.view); 
51588         this.view.render  &&  this.view.render(false, '');  
51589     }
51590     
51591     
51592     this.fireEvent('render', this);
51593 };
51594
51595 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51596     tabTip:'',
51597     setRegion : function(region){
51598         this.region = region;
51599         if(region){
51600            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51601         }else{
51602            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51603         } 
51604     },
51605     
51606     /**
51607      * Returns the toolbar for this Panel if one was configured. 
51608      * @return {Roo.Toolbar} 
51609      */
51610     getToolbar : function(){
51611         return this.toolbar;
51612     },
51613     
51614     setActiveState : function(active){
51615         this.active = active;
51616         if(!active){
51617             this.fireEvent("deactivate", this);
51618         }else{
51619             this.fireEvent("activate", this);
51620         }
51621     },
51622     /**
51623      * Updates this panel's element
51624      * @param {String} content The new content
51625      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51626     */
51627     setContent : function(content, loadScripts){
51628         this.el.update(content, loadScripts);
51629     },
51630
51631     ignoreResize : function(w, h){
51632         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51633             return true;
51634         }else{
51635             this.lastSize = {width: w, height: h};
51636             return false;
51637         }
51638     },
51639     /**
51640      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51641      * @return {Roo.UpdateManager} The UpdateManager
51642      */
51643     getUpdateManager : function(){
51644         return this.el.getUpdateManager();
51645     },
51646      /**
51647      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51648      * @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:
51649 <pre><code>
51650 panel.load({
51651     url: "your-url.php",
51652     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51653     callback: yourFunction,
51654     scope: yourObject, //(optional scope)
51655     discardUrl: false,
51656     nocache: false,
51657     text: "Loading...",
51658     timeout: 30,
51659     scripts: false
51660 });
51661 </code></pre>
51662      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51663      * 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.
51664      * @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}
51665      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51666      * @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.
51667      * @return {Roo.ContentPanel} this
51668      */
51669     load : function(){
51670         var um = this.el.getUpdateManager();
51671         um.update.apply(um, arguments);
51672         return this;
51673     },
51674
51675
51676     /**
51677      * 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.
51678      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51679      * @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)
51680      * @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)
51681      * @return {Roo.UpdateManager} The UpdateManager
51682      */
51683     setUrl : function(url, params, loadOnce){
51684         if(this.refreshDelegate){
51685             this.removeListener("activate", this.refreshDelegate);
51686         }
51687         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51688         this.on("activate", this.refreshDelegate);
51689         return this.el.getUpdateManager();
51690     },
51691     
51692     _handleRefresh : function(url, params, loadOnce){
51693         if(!loadOnce || !this.loaded){
51694             var updater = this.el.getUpdateManager();
51695             updater.update(url, params, this._setLoaded.createDelegate(this));
51696         }
51697     },
51698     
51699     _setLoaded : function(){
51700         this.loaded = true;
51701     }, 
51702     
51703     /**
51704      * Returns this panel's id
51705      * @return {String} 
51706      */
51707     getId : function(){
51708         return this.el.id;
51709     },
51710     
51711     /** 
51712      * Returns this panel's element - used by regiosn to add.
51713      * @return {Roo.Element} 
51714      */
51715     getEl : function(){
51716         return this.wrapEl || this.el;
51717     },
51718     
51719     adjustForComponents : function(width, height)
51720     {
51721         //Roo.log('adjustForComponents ');
51722         if(this.resizeEl != this.el){
51723             width -= this.el.getFrameWidth('lr');
51724             height -= this.el.getFrameWidth('tb');
51725         }
51726         if(this.toolbar){
51727             var te = this.toolbar.getEl();
51728             height -= te.getHeight();
51729             te.setWidth(width);
51730         }
51731         if(this.footer){
51732             var te = this.footer.getEl();
51733             Roo.log("footer:" + te.getHeight());
51734             
51735             height -= te.getHeight();
51736             te.setWidth(width);
51737         }
51738         
51739         
51740         if(this.adjustments){
51741             width += this.adjustments[0];
51742             height += this.adjustments[1];
51743         }
51744         return {"width": width, "height": height};
51745     },
51746     
51747     setSize : function(width, height){
51748         if(this.fitToFrame && !this.ignoreResize(width, height)){
51749             if(this.fitContainer && this.resizeEl != this.el){
51750                 this.el.setSize(width, height);
51751             }
51752             var size = this.adjustForComponents(width, height);
51753             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51754             this.fireEvent('resize', this, size.width, size.height);
51755         }
51756     },
51757     
51758     /**
51759      * Returns this panel's title
51760      * @return {String} 
51761      */
51762     getTitle : function(){
51763         return this.title;
51764     },
51765     
51766     /**
51767      * Set this panel's title
51768      * @param {String} title
51769      */
51770     setTitle : function(title){
51771         this.title = title;
51772         if(this.region){
51773             this.region.updatePanelTitle(this, title);
51774         }
51775     },
51776     
51777     /**
51778      * Returns true is this panel was configured to be closable
51779      * @return {Boolean} 
51780      */
51781     isClosable : function(){
51782         return this.closable;
51783     },
51784     
51785     beforeSlide : function(){
51786         this.el.clip();
51787         this.resizeEl.clip();
51788     },
51789     
51790     afterSlide : function(){
51791         this.el.unclip();
51792         this.resizeEl.unclip();
51793     },
51794     
51795     /**
51796      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51797      *   Will fail silently if the {@link #setUrl} method has not been called.
51798      *   This does not activate the panel, just updates its content.
51799      */
51800     refresh : function(){
51801         if(this.refreshDelegate){
51802            this.loaded = false;
51803            this.refreshDelegate();
51804         }
51805     },
51806     
51807     /**
51808      * Destroys this panel
51809      */
51810     destroy : function(){
51811         this.el.removeAllListeners();
51812         var tempEl = document.createElement("span");
51813         tempEl.appendChild(this.el.dom);
51814         tempEl.innerHTML = "";
51815         this.el.remove();
51816         this.el = null;
51817     },
51818     
51819     /**
51820      * form - if the content panel contains a form - this is a reference to it.
51821      * @type {Roo.form.Form}
51822      */
51823     form : false,
51824     /**
51825      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51826      *    This contains a reference to it.
51827      * @type {Roo.View}
51828      */
51829     view : false,
51830     
51831       /**
51832      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51833      * <pre><code>
51834
51835 layout.addxtype({
51836        xtype : 'Form',
51837        items: [ .... ]
51838    }
51839 );
51840
51841 </code></pre>
51842      * @param {Object} cfg Xtype definition of item to add.
51843      */
51844     
51845     addxtype : function(cfg) {
51846         // add form..
51847         if (cfg.xtype.match(/^Form$/)) {
51848             
51849             var el;
51850             //if (this.footer) {
51851             //    el = this.footer.container.insertSibling(false, 'before');
51852             //} else {
51853                 el = this.el.createChild();
51854             //}
51855
51856             this.form = new  Roo.form.Form(cfg);
51857             
51858             
51859             if ( this.form.allItems.length) this.form.render(el.dom);
51860             return this.form;
51861         }
51862         // should only have one of theses..
51863         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51864             // views.. should not be just added - used named prop 'view''
51865             
51866             cfg.el = this.el.appendChild(document.createElement("div"));
51867             // factory?
51868             
51869             var ret = new Roo.factory(cfg);
51870              
51871              ret.render && ret.render(false, ''); // render blank..
51872             this.view = ret;
51873             return ret;
51874         }
51875         return false;
51876     }
51877 });
51878
51879 /**
51880  * @class Roo.GridPanel
51881  * @extends Roo.ContentPanel
51882  * @constructor
51883  * Create a new GridPanel.
51884  * @param {Roo.grid.Grid} grid The grid for this panel
51885  * @param {String/Object} config A string to set only the panel's title, or a config object
51886  */
51887 Roo.GridPanel = function(grid, config){
51888     
51889   
51890     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51891         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51892         
51893     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51894     
51895     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51896     
51897     if(this.toolbar){
51898         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51899     }
51900     // xtype created footer. - not sure if will work as we normally have to render first..
51901     if (this.footer && !this.footer.el && this.footer.xtype) {
51902         
51903         this.footer.container = this.grid.getView().getFooterPanel(true);
51904         this.footer.dataSource = this.grid.dataSource;
51905         this.footer = Roo.factory(this.footer, Roo);
51906         
51907     }
51908     
51909     grid.monitorWindowResize = false; // turn off autosizing
51910     grid.autoHeight = false;
51911     grid.autoWidth = false;
51912     this.grid = grid;
51913     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51914 };
51915
51916 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51917     getId : function(){
51918         return this.grid.id;
51919     },
51920     
51921     /**
51922      * Returns the grid for this panel
51923      * @return {Roo.grid.Grid} 
51924      */
51925     getGrid : function(){
51926         return this.grid;    
51927     },
51928     
51929     setSize : function(width, height){
51930         if(!this.ignoreResize(width, height)){
51931             var grid = this.grid;
51932             var size = this.adjustForComponents(width, height);
51933             grid.getGridEl().setSize(size.width, size.height);
51934             grid.autoSize();
51935         }
51936     },
51937     
51938     beforeSlide : function(){
51939         this.grid.getView().scroller.clip();
51940     },
51941     
51942     afterSlide : function(){
51943         this.grid.getView().scroller.unclip();
51944     },
51945     
51946     destroy : function(){
51947         this.grid.destroy();
51948         delete this.grid;
51949         Roo.GridPanel.superclass.destroy.call(this); 
51950     }
51951 });
51952
51953
51954 /**
51955  * @class Roo.NestedLayoutPanel
51956  * @extends Roo.ContentPanel
51957  * @constructor
51958  * Create a new NestedLayoutPanel.
51959  * 
51960  * 
51961  * @param {Roo.BorderLayout} layout The layout for this panel
51962  * @param {String/Object} config A string to set only the title or a config object
51963  */
51964 Roo.NestedLayoutPanel = function(layout, config)
51965 {
51966     // construct with only one argument..
51967     /* FIXME - implement nicer consturctors
51968     if (layout.layout) {
51969         config = layout;
51970         layout = config.layout;
51971         delete config.layout;
51972     }
51973     if (layout.xtype && !layout.getEl) {
51974         // then layout needs constructing..
51975         layout = Roo.factory(layout, Roo);
51976     }
51977     */
51978     
51979     
51980     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51981     
51982     layout.monitorWindowResize = false; // turn off autosizing
51983     this.layout = layout;
51984     this.layout.getEl().addClass("x-layout-nested-layout");
51985     
51986     
51987     
51988     
51989 };
51990
51991 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51992
51993     setSize : function(width, height){
51994         if(!this.ignoreResize(width, height)){
51995             var size = this.adjustForComponents(width, height);
51996             var el = this.layout.getEl();
51997             el.setSize(size.width, size.height);
51998             var touch = el.dom.offsetWidth;
51999             this.layout.layout();
52000             // ie requires a double layout on the first pass
52001             if(Roo.isIE && !this.initialized){
52002                 this.initialized = true;
52003                 this.layout.layout();
52004             }
52005         }
52006     },
52007     
52008     // activate all subpanels if not currently active..
52009     
52010     setActiveState : function(active){
52011         this.active = active;
52012         if(!active){
52013             this.fireEvent("deactivate", this);
52014             return;
52015         }
52016         
52017         this.fireEvent("activate", this);
52018         // not sure if this should happen before or after..
52019         if (!this.layout) {
52020             return; // should not happen..
52021         }
52022         var reg = false;
52023         for (var r in this.layout.regions) {
52024             reg = this.layout.getRegion(r);
52025             if (reg.getActivePanel()) {
52026                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52027                 reg.setActivePanel(reg.getActivePanel());
52028                 continue;
52029             }
52030             if (!reg.panels.length) {
52031                 continue;
52032             }
52033             reg.showPanel(reg.getPanel(0));
52034         }
52035         
52036         
52037         
52038         
52039     },
52040     
52041     /**
52042      * Returns the nested BorderLayout for this panel
52043      * @return {Roo.BorderLayout} 
52044      */
52045     getLayout : function(){
52046         return this.layout;
52047     },
52048     
52049      /**
52050      * Adds a xtype elements to the layout of the nested panel
52051      * <pre><code>
52052
52053 panel.addxtype({
52054        xtype : 'ContentPanel',
52055        region: 'west',
52056        items: [ .... ]
52057    }
52058 );
52059
52060 panel.addxtype({
52061         xtype : 'NestedLayoutPanel',
52062         region: 'west',
52063         layout: {
52064            center: { },
52065            west: { }   
52066         },
52067         items : [ ... list of content panels or nested layout panels.. ]
52068    }
52069 );
52070 </code></pre>
52071      * @param {Object} cfg Xtype definition of item to add.
52072      */
52073     addxtype : function(cfg) {
52074         return this.layout.addxtype(cfg);
52075     
52076     }
52077 });
52078
52079 Roo.ScrollPanel = function(el, config, content){
52080     config = config || {};
52081     config.fitToFrame = true;
52082     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52083     
52084     this.el.dom.style.overflow = "hidden";
52085     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52086     this.el.removeClass("x-layout-inactive-content");
52087     this.el.on("mousewheel", this.onWheel, this);
52088
52089     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52090     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52091     up.unselectable(); down.unselectable();
52092     up.on("click", this.scrollUp, this);
52093     down.on("click", this.scrollDown, this);
52094     up.addClassOnOver("x-scroller-btn-over");
52095     down.addClassOnOver("x-scroller-btn-over");
52096     up.addClassOnClick("x-scroller-btn-click");
52097     down.addClassOnClick("x-scroller-btn-click");
52098     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52099
52100     this.resizeEl = this.el;
52101     this.el = wrap; this.up = up; this.down = down;
52102 };
52103
52104 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52105     increment : 100,
52106     wheelIncrement : 5,
52107     scrollUp : function(){
52108         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52109     },
52110
52111     scrollDown : function(){
52112         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52113     },
52114
52115     afterScroll : function(){
52116         var el = this.resizeEl;
52117         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52118         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52119         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52120     },
52121
52122     setSize : function(){
52123         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52124         this.afterScroll();
52125     },
52126
52127     onWheel : function(e){
52128         var d = e.getWheelDelta();
52129         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52130         this.afterScroll();
52131         e.stopEvent();
52132     },
52133
52134     setContent : function(content, loadScripts){
52135         this.resizeEl.update(content, loadScripts);
52136     }
52137
52138 });
52139
52140
52141
52142
52143
52144
52145
52146
52147
52148 /**
52149  * @class Roo.TreePanel
52150  * @extends Roo.ContentPanel
52151  * @constructor
52152  * Create a new TreePanel. - defaults to fit/scoll contents.
52153  * @param {String/Object} config A string to set only the panel's title, or a config object
52154  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52155  */
52156 Roo.TreePanel = function(config){
52157     var el = config.el;
52158     var tree = config.tree;
52159     delete config.tree; 
52160     delete config.el; // hopefull!
52161     
52162     // wrapper for IE7 strict & safari scroll issue
52163     
52164     var treeEl = el.createChild();
52165     config.resizeEl = treeEl;
52166     
52167     
52168     
52169     Roo.TreePanel.superclass.constructor.call(this, el, config);
52170  
52171  
52172     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52173     //console.log(tree);
52174     this.on('activate', function()
52175     {
52176         if (this.tree.rendered) {
52177             return;
52178         }
52179         //console.log('render tree');
52180         this.tree.render();
52181     });
52182     // this should not be needed.. - it's actually the 'el' that resizes?
52183     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52184     
52185     //this.on('resize',  function (cp, w, h) {
52186     //        this.tree.innerCt.setWidth(w);
52187     //        this.tree.innerCt.setHeight(h);
52188     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52189     //});
52190
52191         
52192     
52193 };
52194
52195 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52196     fitToFrame : true,
52197     autoScroll : true
52198 });
52199
52200
52201
52202
52203
52204
52205
52206
52207
52208
52209
52210 /*
52211  * Based on:
52212  * Ext JS Library 1.1.1
52213  * Copyright(c) 2006-2007, Ext JS, LLC.
52214  *
52215  * Originally Released Under LGPL - original licence link has changed is not relivant.
52216  *
52217  * Fork - LGPL
52218  * <script type="text/javascript">
52219  */
52220  
52221
52222 /**
52223  * @class Roo.ReaderLayout
52224  * @extends Roo.BorderLayout
52225  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52226  * center region containing two nested regions (a top one for a list view and one for item preview below),
52227  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52228  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52229  * expedites the setup of the overall layout and regions for this common application style.
52230  * Example:
52231  <pre><code>
52232 var reader = new Roo.ReaderLayout();
52233 var CP = Roo.ContentPanel;  // shortcut for adding
52234
52235 reader.beginUpdate();
52236 reader.add("north", new CP("north", "North"));
52237 reader.add("west", new CP("west", {title: "West"}));
52238 reader.add("east", new CP("east", {title: "East"}));
52239
52240 reader.regions.listView.add(new CP("listView", "List"));
52241 reader.regions.preview.add(new CP("preview", "Preview"));
52242 reader.endUpdate();
52243 </code></pre>
52244 * @constructor
52245 * Create a new ReaderLayout
52246 * @param {Object} config Configuration options
52247 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52248 * document.body if omitted)
52249 */
52250 Roo.ReaderLayout = function(config, renderTo){
52251     var c = config || {size:{}};
52252     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52253         north: c.north !== false ? Roo.apply({
52254             split:false,
52255             initialSize: 32,
52256             titlebar: false
52257         }, c.north) : false,
52258         west: c.west !== false ? Roo.apply({
52259             split:true,
52260             initialSize: 200,
52261             minSize: 175,
52262             maxSize: 400,
52263             titlebar: true,
52264             collapsible: true,
52265             animate: true,
52266             margins:{left:5,right:0,bottom:5,top:5},
52267             cmargins:{left:5,right:5,bottom:5,top:5}
52268         }, c.west) : false,
52269         east: c.east !== false ? Roo.apply({
52270             split:true,
52271             initialSize: 200,
52272             minSize: 175,
52273             maxSize: 400,
52274             titlebar: true,
52275             collapsible: true,
52276             animate: true,
52277             margins:{left:0,right:5,bottom:5,top:5},
52278             cmargins:{left:5,right:5,bottom:5,top:5}
52279         }, c.east) : false,
52280         center: Roo.apply({
52281             tabPosition: 'top',
52282             autoScroll:false,
52283             closeOnTab: true,
52284             titlebar:false,
52285             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52286         }, c.center)
52287     });
52288
52289     this.el.addClass('x-reader');
52290
52291     this.beginUpdate();
52292
52293     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52294         south: c.preview !== false ? Roo.apply({
52295             split:true,
52296             initialSize: 200,
52297             minSize: 100,
52298             autoScroll:true,
52299             collapsible:true,
52300             titlebar: true,
52301             cmargins:{top:5,left:0, right:0, bottom:0}
52302         }, c.preview) : false,
52303         center: Roo.apply({
52304             autoScroll:false,
52305             titlebar:false,
52306             minHeight:200
52307         }, c.listView)
52308     });
52309     this.add('center', new Roo.NestedLayoutPanel(inner,
52310             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52311
52312     this.endUpdate();
52313
52314     this.regions.preview = inner.getRegion('south');
52315     this.regions.listView = inner.getRegion('center');
52316 };
52317
52318 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52319  * Based on:
52320  * Ext JS Library 1.1.1
52321  * Copyright(c) 2006-2007, Ext JS, LLC.
52322  *
52323  * Originally Released Under LGPL - original licence link has changed is not relivant.
52324  *
52325  * Fork - LGPL
52326  * <script type="text/javascript">
52327  */
52328  
52329 /**
52330  * @class Roo.grid.Grid
52331  * @extends Roo.util.Observable
52332  * This class represents the primary interface of a component based grid control.
52333  * <br><br>Usage:<pre><code>
52334  var grid = new Roo.grid.Grid("my-container-id", {
52335      ds: myDataStore,
52336      cm: myColModel,
52337      selModel: mySelectionModel,
52338      autoSizeColumns: true,
52339      monitorWindowResize: false,
52340      trackMouseOver: true
52341  });
52342  // set any options
52343  grid.render();
52344  * </code></pre>
52345  * <b>Common Problems:</b><br/>
52346  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52347  * element will correct this<br/>
52348  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52349  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52350  * are unpredictable.<br/>
52351  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52352  * grid to calculate dimensions/offsets.<br/>
52353   * @constructor
52354  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52355  * The container MUST have some type of size defined for the grid to fill. The container will be
52356  * automatically set to position relative if it isn't already.
52357  * @param {Object} config A config object that sets properties on this grid.
52358  */
52359 Roo.grid.Grid = function(container, config){
52360         // initialize the container
52361         this.container = Roo.get(container);
52362         this.container.update("");
52363         this.container.setStyle("overflow", "hidden");
52364     this.container.addClass('x-grid-container');
52365
52366     this.id = this.container.id;
52367
52368     Roo.apply(this, config);
52369     // check and correct shorthanded configs
52370     if(this.ds){
52371         this.dataSource = this.ds;
52372         delete this.ds;
52373     }
52374     if(this.cm){
52375         this.colModel = this.cm;
52376         delete this.cm;
52377     }
52378     if(this.sm){
52379         this.selModel = this.sm;
52380         delete this.sm;
52381     }
52382
52383     if (this.selModel) {
52384         this.selModel = Roo.factory(this.selModel, Roo.grid);
52385         this.sm = this.selModel;
52386         this.sm.xmodule = this.xmodule || false;
52387     }
52388     if (typeof(this.colModel.config) == 'undefined') {
52389         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52390         this.cm = this.colModel;
52391         this.cm.xmodule = this.xmodule || false;
52392     }
52393     if (this.dataSource) {
52394         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52395         this.ds = this.dataSource;
52396         this.ds.xmodule = this.xmodule || false;
52397          
52398     }
52399     
52400     
52401     
52402     if(this.width){
52403         this.container.setWidth(this.width);
52404     }
52405
52406     if(this.height){
52407         this.container.setHeight(this.height);
52408     }
52409     /** @private */
52410         this.addEvents({
52411         // raw events
52412         /**
52413          * @event click
52414          * The raw click event for the entire grid.
52415          * @param {Roo.EventObject} e
52416          */
52417         "click" : true,
52418         /**
52419          * @event dblclick
52420          * The raw dblclick event for the entire grid.
52421          * @param {Roo.EventObject} e
52422          */
52423         "dblclick" : true,
52424         /**
52425          * @event contextmenu
52426          * The raw contextmenu event for the entire grid.
52427          * @param {Roo.EventObject} e
52428          */
52429         "contextmenu" : true,
52430         /**
52431          * @event mousedown
52432          * The raw mousedown event for the entire grid.
52433          * @param {Roo.EventObject} e
52434          */
52435         "mousedown" : true,
52436         /**
52437          * @event mouseup
52438          * The raw mouseup event for the entire grid.
52439          * @param {Roo.EventObject} e
52440          */
52441         "mouseup" : true,
52442         /**
52443          * @event mouseover
52444          * The raw mouseover event for the entire grid.
52445          * @param {Roo.EventObject} e
52446          */
52447         "mouseover" : true,
52448         /**
52449          * @event mouseout
52450          * The raw mouseout event for the entire grid.
52451          * @param {Roo.EventObject} e
52452          */
52453         "mouseout" : true,
52454         /**
52455          * @event keypress
52456          * The raw keypress event for the entire grid.
52457          * @param {Roo.EventObject} e
52458          */
52459         "keypress" : true,
52460         /**
52461          * @event keydown
52462          * The raw keydown event for the entire grid.
52463          * @param {Roo.EventObject} e
52464          */
52465         "keydown" : true,
52466
52467         // custom events
52468
52469         /**
52470          * @event cellclick
52471          * Fires when a cell is clicked
52472          * @param {Grid} this
52473          * @param {Number} rowIndex
52474          * @param {Number} columnIndex
52475          * @param {Roo.EventObject} e
52476          */
52477         "cellclick" : true,
52478         /**
52479          * @event celldblclick
52480          * Fires when a cell is double clicked
52481          * @param {Grid} this
52482          * @param {Number} rowIndex
52483          * @param {Number} columnIndex
52484          * @param {Roo.EventObject} e
52485          */
52486         "celldblclick" : true,
52487         /**
52488          * @event rowclick
52489          * Fires when a row is clicked
52490          * @param {Grid} this
52491          * @param {Number} rowIndex
52492          * @param {Roo.EventObject} e
52493          */
52494         "rowclick" : true,
52495         /**
52496          * @event rowdblclick
52497          * Fires when a row is double clicked
52498          * @param {Grid} this
52499          * @param {Number} rowIndex
52500          * @param {Roo.EventObject} e
52501          */
52502         "rowdblclick" : true,
52503         /**
52504          * @event headerclick
52505          * Fires when a header is clicked
52506          * @param {Grid} this
52507          * @param {Number} columnIndex
52508          * @param {Roo.EventObject} e
52509          */
52510         "headerclick" : true,
52511         /**
52512          * @event headerdblclick
52513          * Fires when a header cell is double clicked
52514          * @param {Grid} this
52515          * @param {Number} columnIndex
52516          * @param {Roo.EventObject} e
52517          */
52518         "headerdblclick" : true,
52519         /**
52520          * @event rowcontextmenu
52521          * Fires when a row is right clicked
52522          * @param {Grid} this
52523          * @param {Number} rowIndex
52524          * @param {Roo.EventObject} e
52525          */
52526         "rowcontextmenu" : true,
52527         /**
52528          * @event cellcontextmenu
52529          * Fires when a cell is right clicked
52530          * @param {Grid} this
52531          * @param {Number} rowIndex
52532          * @param {Number} cellIndex
52533          * @param {Roo.EventObject} e
52534          */
52535          "cellcontextmenu" : true,
52536         /**
52537          * @event headercontextmenu
52538          * Fires when a header is right clicked
52539          * @param {Grid} this
52540          * @param {Number} columnIndex
52541          * @param {Roo.EventObject} e
52542          */
52543         "headercontextmenu" : true,
52544         /**
52545          * @event bodyscroll
52546          * Fires when the body element is scrolled
52547          * @param {Number} scrollLeft
52548          * @param {Number} scrollTop
52549          */
52550         "bodyscroll" : true,
52551         /**
52552          * @event columnresize
52553          * Fires when the user resizes a column
52554          * @param {Number} columnIndex
52555          * @param {Number} newSize
52556          */
52557         "columnresize" : true,
52558         /**
52559          * @event columnmove
52560          * Fires when the user moves a column
52561          * @param {Number} oldIndex
52562          * @param {Number} newIndex
52563          */
52564         "columnmove" : true,
52565         /**
52566          * @event startdrag
52567          * Fires when row(s) start being dragged
52568          * @param {Grid} this
52569          * @param {Roo.GridDD} dd The drag drop object
52570          * @param {event} e The raw browser event
52571          */
52572         "startdrag" : true,
52573         /**
52574          * @event enddrag
52575          * Fires when a drag operation is complete
52576          * @param {Grid} this
52577          * @param {Roo.GridDD} dd The drag drop object
52578          * @param {event} e The raw browser event
52579          */
52580         "enddrag" : true,
52581         /**
52582          * @event dragdrop
52583          * Fires when dragged row(s) are dropped on a valid DD target
52584          * @param {Grid} this
52585          * @param {Roo.GridDD} dd The drag drop object
52586          * @param {String} targetId The target drag drop object
52587          * @param {event} e The raw browser event
52588          */
52589         "dragdrop" : true,
52590         /**
52591          * @event dragover
52592          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52593          * @param {Grid} this
52594          * @param {Roo.GridDD} dd The drag drop object
52595          * @param {String} targetId The target drag drop object
52596          * @param {event} e The raw browser event
52597          */
52598         "dragover" : true,
52599         /**
52600          * @event dragenter
52601          *  Fires when the dragged row(s) first cross another DD target while being dragged
52602          * @param {Grid} this
52603          * @param {Roo.GridDD} dd The drag drop object
52604          * @param {String} targetId The target drag drop object
52605          * @param {event} e The raw browser event
52606          */
52607         "dragenter" : true,
52608         /**
52609          * @event dragout
52610          * Fires when the dragged row(s) leave another DD target while being dragged
52611          * @param {Grid} this
52612          * @param {Roo.GridDD} dd The drag drop object
52613          * @param {String} targetId The target drag drop object
52614          * @param {event} e The raw browser event
52615          */
52616         "dragout" : true,
52617         /**
52618          * @event rowclass
52619          * Fires when a row is rendered, so you can change add a style to it.
52620          * @param {GridView} gridview   The grid view
52621          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52622          */
52623         'rowclass' : true,
52624
52625         /**
52626          * @event render
52627          * Fires when the grid is rendered
52628          * @param {Grid} grid
52629          */
52630         'render' : true
52631     });
52632
52633     Roo.grid.Grid.superclass.constructor.call(this);
52634 };
52635 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52636     
52637     /**
52638      * @cfg {String} ddGroup - drag drop group.
52639      */
52640
52641     /**
52642      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52643      */
52644     minColumnWidth : 25,
52645
52646     /**
52647      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52648      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52649      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52650      */
52651     autoSizeColumns : false,
52652
52653     /**
52654      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52655      */
52656     autoSizeHeaders : true,
52657
52658     /**
52659      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52660      */
52661     monitorWindowResize : true,
52662
52663     /**
52664      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52665      * rows measured to get a columns size. Default is 0 (all rows).
52666      */
52667     maxRowsToMeasure : 0,
52668
52669     /**
52670      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52671      */
52672     trackMouseOver : true,
52673
52674     /**
52675     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52676     */
52677     
52678     /**
52679     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52680     */
52681     enableDragDrop : false,
52682     
52683     /**
52684     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52685     */
52686     enableColumnMove : true,
52687     
52688     /**
52689     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52690     */
52691     enableColumnHide : true,
52692     
52693     /**
52694     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52695     */
52696     enableRowHeightSync : false,
52697     
52698     /**
52699     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52700     */
52701     stripeRows : true,
52702     
52703     /**
52704     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52705     */
52706     autoHeight : false,
52707
52708     /**
52709      * @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.
52710      */
52711     autoExpandColumn : false,
52712
52713     /**
52714     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52715     * Default is 50.
52716     */
52717     autoExpandMin : 50,
52718
52719     /**
52720     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52721     */
52722     autoExpandMax : 1000,
52723
52724     /**
52725     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52726     */
52727     view : null,
52728
52729     /**
52730     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52731     */
52732     loadMask : false,
52733     /**
52734     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52735     */
52736     dropTarget: false,
52737     
52738    
52739     
52740     // private
52741     rendered : false,
52742
52743     /**
52744     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52745     * of a fixed width. Default is false.
52746     */
52747     /**
52748     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52749     */
52750     /**
52751      * Called once after all setup has been completed and the grid is ready to be rendered.
52752      * @return {Roo.grid.Grid} this
52753      */
52754     render : function()
52755     {
52756         var c = this.container;
52757         // try to detect autoHeight/width mode
52758         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52759             this.autoHeight = true;
52760         }
52761         var view = this.getView();
52762         view.init(this);
52763
52764         c.on("click", this.onClick, this);
52765         c.on("dblclick", this.onDblClick, this);
52766         c.on("contextmenu", this.onContextMenu, this);
52767         c.on("keydown", this.onKeyDown, this);
52768         if (Roo.isTouch) {
52769             c.on("touchstart", this.onTouchStart, this);
52770         }
52771
52772         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52773
52774         this.getSelectionModel().init(this);
52775
52776         view.render();
52777
52778         if(this.loadMask){
52779             this.loadMask = new Roo.LoadMask(this.container,
52780                     Roo.apply({store:this.dataSource}, this.loadMask));
52781         }
52782         
52783         
52784         if (this.toolbar && this.toolbar.xtype) {
52785             this.toolbar.container = this.getView().getHeaderPanel(true);
52786             this.toolbar = new Roo.Toolbar(this.toolbar);
52787         }
52788         if (this.footer && this.footer.xtype) {
52789             this.footer.dataSource = this.getDataSource();
52790             this.footer.container = this.getView().getFooterPanel(true);
52791             this.footer = Roo.factory(this.footer, Roo);
52792         }
52793         if (this.dropTarget && this.dropTarget.xtype) {
52794             delete this.dropTarget.xtype;
52795             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52796         }
52797         
52798         
52799         this.rendered = true;
52800         this.fireEvent('render', this);
52801         return this;
52802     },
52803
52804         /**
52805          * Reconfigures the grid to use a different Store and Column Model.
52806          * The View will be bound to the new objects and refreshed.
52807          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52808          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52809          */
52810     reconfigure : function(dataSource, colModel){
52811         if(this.loadMask){
52812             this.loadMask.destroy();
52813             this.loadMask = new Roo.LoadMask(this.container,
52814                     Roo.apply({store:dataSource}, this.loadMask));
52815         }
52816         this.view.bind(dataSource, colModel);
52817         this.dataSource = dataSource;
52818         this.colModel = colModel;
52819         this.view.refresh(true);
52820     },
52821
52822     // private
52823     onKeyDown : function(e){
52824         this.fireEvent("keydown", e);
52825     },
52826
52827     /**
52828      * Destroy this grid.
52829      * @param {Boolean} removeEl True to remove the element
52830      */
52831     destroy : function(removeEl, keepListeners){
52832         if(this.loadMask){
52833             this.loadMask.destroy();
52834         }
52835         var c = this.container;
52836         c.removeAllListeners();
52837         this.view.destroy();
52838         this.colModel.purgeListeners();
52839         if(!keepListeners){
52840             this.purgeListeners();
52841         }
52842         c.update("");
52843         if(removeEl === true){
52844             c.remove();
52845         }
52846     },
52847
52848     // private
52849     processEvent : function(name, e){
52850         // does this fire select???
52851         //Roo.log('grid:processEvent '  + name);
52852         
52853         if (name != 'touchstart' ) {
52854             this.fireEvent(name, e);    
52855         }
52856         
52857         var t = e.getTarget();
52858         var v = this.view;
52859         var header = v.findHeaderIndex(t);
52860         if(header !== false){
52861             var ename = name == 'touchstart' ? 'click' : name;
52862              
52863             this.fireEvent("header" + ename, this, header, e);
52864         }else{
52865             var row = v.findRowIndex(t);
52866             var cell = v.findCellIndex(t);
52867             if (name == 'touchstart') {
52868                 // first touch is always a click.
52869                 // hopefull this happens after selection is updated.?
52870                 name = false;
52871                 
52872                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52873                     var cs = this.selModel.getSelectedCell();
52874                     if (row == cs[0] && cell == cs[1]){
52875                         name = 'dblclick';
52876                     }
52877                 }
52878                 if (typeof(this.selModel.getSelections) != 'undefined') {
52879                     var cs = this.selModel.getSelections();
52880                     var ds = this.dataSource;
52881                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52882                         name = 'dblclick';
52883                     }
52884                 }
52885                 if (!name) {
52886                     return;
52887                 }
52888             }
52889             
52890             
52891             if(row !== false){
52892                 this.fireEvent("row" + name, this, row, e);
52893                 if(cell !== false){
52894                     this.fireEvent("cell" + name, this, row, cell, e);
52895                 }
52896             }
52897         }
52898     },
52899
52900     // private
52901     onClick : function(e){
52902         this.processEvent("click", e);
52903     },
52904    // private
52905     onTouchStart : function(e){
52906         this.processEvent("touchstart", e);
52907     },
52908
52909     // private
52910     onContextMenu : function(e, t){
52911         this.processEvent("contextmenu", e);
52912     },
52913
52914     // private
52915     onDblClick : function(e){
52916         this.processEvent("dblclick", e);
52917     },
52918
52919     // private
52920     walkCells : function(row, col, step, fn, scope){
52921         var cm = this.colModel, clen = cm.getColumnCount();
52922         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52923         if(step < 0){
52924             if(col < 0){
52925                 row--;
52926                 first = false;
52927             }
52928             while(row >= 0){
52929                 if(!first){
52930                     col = clen-1;
52931                 }
52932                 first = false;
52933                 while(col >= 0){
52934                     if(fn.call(scope || this, row, col, cm) === true){
52935                         return [row, col];
52936                     }
52937                     col--;
52938                 }
52939                 row--;
52940             }
52941         } else {
52942             if(col >= clen){
52943                 row++;
52944                 first = false;
52945             }
52946             while(row < rlen){
52947                 if(!first){
52948                     col = 0;
52949                 }
52950                 first = false;
52951                 while(col < clen){
52952                     if(fn.call(scope || this, row, col, cm) === true){
52953                         return [row, col];
52954                     }
52955                     col++;
52956                 }
52957                 row++;
52958             }
52959         }
52960         return null;
52961     },
52962
52963     // private
52964     getSelections : function(){
52965         return this.selModel.getSelections();
52966     },
52967
52968     /**
52969      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52970      * but if manual update is required this method will initiate it.
52971      */
52972     autoSize : function(){
52973         if(this.rendered){
52974             this.view.layout();
52975             if(this.view.adjustForScroll){
52976                 this.view.adjustForScroll();
52977             }
52978         }
52979     },
52980
52981     /**
52982      * Returns the grid's underlying element.
52983      * @return {Element} The element
52984      */
52985     getGridEl : function(){
52986         return this.container;
52987     },
52988
52989     // private for compatibility, overridden by editor grid
52990     stopEditing : function(){},
52991
52992     /**
52993      * Returns the grid's SelectionModel.
52994      * @return {SelectionModel}
52995      */
52996     getSelectionModel : function(){
52997         if(!this.selModel){
52998             this.selModel = new Roo.grid.RowSelectionModel();
52999         }
53000         return this.selModel;
53001     },
53002
53003     /**
53004      * Returns the grid's DataSource.
53005      * @return {DataSource}
53006      */
53007     getDataSource : function(){
53008         return this.dataSource;
53009     },
53010
53011     /**
53012      * Returns the grid's ColumnModel.
53013      * @return {ColumnModel}
53014      */
53015     getColumnModel : function(){
53016         return this.colModel;
53017     },
53018
53019     /**
53020      * Returns the grid's GridView object.
53021      * @return {GridView}
53022      */
53023     getView : function(){
53024         if(!this.view){
53025             this.view = new Roo.grid.GridView(this.viewConfig);
53026         }
53027         return this.view;
53028     },
53029     /**
53030      * Called to get grid's drag proxy text, by default returns this.ddText.
53031      * @return {String}
53032      */
53033     getDragDropText : function(){
53034         var count = this.selModel.getCount();
53035         return String.format(this.ddText, count, count == 1 ? '' : 's');
53036     }
53037 });
53038 /**
53039  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53040  * %0 is replaced with the number of selected rows.
53041  * @type String
53042  */
53043 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53044  * Based on:
53045  * Ext JS Library 1.1.1
53046  * Copyright(c) 2006-2007, Ext JS, LLC.
53047  *
53048  * Originally Released Under LGPL - original licence link has changed is not relivant.
53049  *
53050  * Fork - LGPL
53051  * <script type="text/javascript">
53052  */
53053  
53054 Roo.grid.AbstractGridView = function(){
53055         this.grid = null;
53056         
53057         this.events = {
53058             "beforerowremoved" : true,
53059             "beforerowsinserted" : true,
53060             "beforerefresh" : true,
53061             "rowremoved" : true,
53062             "rowsinserted" : true,
53063             "rowupdated" : true,
53064             "refresh" : true
53065         };
53066     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53067 };
53068
53069 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53070     rowClass : "x-grid-row",
53071     cellClass : "x-grid-cell",
53072     tdClass : "x-grid-td",
53073     hdClass : "x-grid-hd",
53074     splitClass : "x-grid-hd-split",
53075     
53076     init: function(grid){
53077         this.grid = grid;
53078                 var cid = this.grid.getGridEl().id;
53079         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53080         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53081         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53082         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53083         },
53084         
53085     getColumnRenderers : function(){
53086         var renderers = [];
53087         var cm = this.grid.colModel;
53088         var colCount = cm.getColumnCount();
53089         for(var i = 0; i < colCount; i++){
53090             renderers[i] = cm.getRenderer(i);
53091         }
53092         return renderers;
53093     },
53094     
53095     getColumnIds : function(){
53096         var ids = [];
53097         var cm = this.grid.colModel;
53098         var colCount = cm.getColumnCount();
53099         for(var i = 0; i < colCount; i++){
53100             ids[i] = cm.getColumnId(i);
53101         }
53102         return ids;
53103     },
53104     
53105     getDataIndexes : function(){
53106         if(!this.indexMap){
53107             this.indexMap = this.buildIndexMap();
53108         }
53109         return this.indexMap.colToData;
53110     },
53111     
53112     getColumnIndexByDataIndex : function(dataIndex){
53113         if(!this.indexMap){
53114             this.indexMap = this.buildIndexMap();
53115         }
53116         return this.indexMap.dataToCol[dataIndex];
53117     },
53118     
53119     /**
53120      * Set a css style for a column dynamically. 
53121      * @param {Number} colIndex The index of the column
53122      * @param {String} name The css property name
53123      * @param {String} value The css value
53124      */
53125     setCSSStyle : function(colIndex, name, value){
53126         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53127         Roo.util.CSS.updateRule(selector, name, value);
53128     },
53129     
53130     generateRules : function(cm){
53131         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53132         Roo.util.CSS.removeStyleSheet(rulesId);
53133         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53134             var cid = cm.getColumnId(i);
53135             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53136                          this.tdSelector, cid, " {\n}\n",
53137                          this.hdSelector, cid, " {\n}\n",
53138                          this.splitSelector, cid, " {\n}\n");
53139         }
53140         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53141     }
53142 });/*
53143  * Based on:
53144  * Ext JS Library 1.1.1
53145  * Copyright(c) 2006-2007, Ext JS, LLC.
53146  *
53147  * Originally Released Under LGPL - original licence link has changed is not relivant.
53148  *
53149  * Fork - LGPL
53150  * <script type="text/javascript">
53151  */
53152
53153 // private
53154 // This is a support class used internally by the Grid components
53155 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53156     this.grid = grid;
53157     this.view = grid.getView();
53158     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53159     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53160     if(hd2){
53161         this.setHandleElId(Roo.id(hd));
53162         this.setOuterHandleElId(Roo.id(hd2));
53163     }
53164     this.scroll = false;
53165 };
53166 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53167     maxDragWidth: 120,
53168     getDragData : function(e){
53169         var t = Roo.lib.Event.getTarget(e);
53170         var h = this.view.findHeaderCell(t);
53171         if(h){
53172             return {ddel: h.firstChild, header:h};
53173         }
53174         return false;
53175     },
53176
53177     onInitDrag : function(e){
53178         this.view.headersDisabled = true;
53179         var clone = this.dragData.ddel.cloneNode(true);
53180         clone.id = Roo.id();
53181         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53182         this.proxy.update(clone);
53183         return true;
53184     },
53185
53186     afterValidDrop : function(){
53187         var v = this.view;
53188         setTimeout(function(){
53189             v.headersDisabled = false;
53190         }, 50);
53191     },
53192
53193     afterInvalidDrop : function(){
53194         var v = this.view;
53195         setTimeout(function(){
53196             v.headersDisabled = false;
53197         }, 50);
53198     }
53199 });
53200 /*
53201  * Based on:
53202  * Ext JS Library 1.1.1
53203  * Copyright(c) 2006-2007, Ext JS, LLC.
53204  *
53205  * Originally Released Under LGPL - original licence link has changed is not relivant.
53206  *
53207  * Fork - LGPL
53208  * <script type="text/javascript">
53209  */
53210 // private
53211 // This is a support class used internally by the Grid components
53212 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53213     this.grid = grid;
53214     this.view = grid.getView();
53215     // split the proxies so they don't interfere with mouse events
53216     this.proxyTop = Roo.DomHelper.append(document.body, {
53217         cls:"col-move-top", html:"&#160;"
53218     }, true);
53219     this.proxyBottom = Roo.DomHelper.append(document.body, {
53220         cls:"col-move-bottom", html:"&#160;"
53221     }, true);
53222     this.proxyTop.hide = this.proxyBottom.hide = function(){
53223         this.setLeftTop(-100,-100);
53224         this.setStyle("visibility", "hidden");
53225     };
53226     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53227     // temporarily disabled
53228     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53229     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53230 };
53231 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53232     proxyOffsets : [-4, -9],
53233     fly: Roo.Element.fly,
53234
53235     getTargetFromEvent : function(e){
53236         var t = Roo.lib.Event.getTarget(e);
53237         var cindex = this.view.findCellIndex(t);
53238         if(cindex !== false){
53239             return this.view.getHeaderCell(cindex);
53240         }
53241         return null;
53242     },
53243
53244     nextVisible : function(h){
53245         var v = this.view, cm = this.grid.colModel;
53246         h = h.nextSibling;
53247         while(h){
53248             if(!cm.isHidden(v.getCellIndex(h))){
53249                 return h;
53250             }
53251             h = h.nextSibling;
53252         }
53253         return null;
53254     },
53255
53256     prevVisible : function(h){
53257         var v = this.view, cm = this.grid.colModel;
53258         h = h.prevSibling;
53259         while(h){
53260             if(!cm.isHidden(v.getCellIndex(h))){
53261                 return h;
53262             }
53263             h = h.prevSibling;
53264         }
53265         return null;
53266     },
53267
53268     positionIndicator : function(h, n, e){
53269         var x = Roo.lib.Event.getPageX(e);
53270         var r = Roo.lib.Dom.getRegion(n.firstChild);
53271         var px, pt, py = r.top + this.proxyOffsets[1];
53272         if((r.right - x) <= (r.right-r.left)/2){
53273             px = r.right+this.view.borderWidth;
53274             pt = "after";
53275         }else{
53276             px = r.left;
53277             pt = "before";
53278         }
53279         var oldIndex = this.view.getCellIndex(h);
53280         var newIndex = this.view.getCellIndex(n);
53281
53282         if(this.grid.colModel.isFixed(newIndex)){
53283             return false;
53284         }
53285
53286         var locked = this.grid.colModel.isLocked(newIndex);
53287
53288         if(pt == "after"){
53289             newIndex++;
53290         }
53291         if(oldIndex < newIndex){
53292             newIndex--;
53293         }
53294         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53295             return false;
53296         }
53297         px +=  this.proxyOffsets[0];
53298         this.proxyTop.setLeftTop(px, py);
53299         this.proxyTop.show();
53300         if(!this.bottomOffset){
53301             this.bottomOffset = this.view.mainHd.getHeight();
53302         }
53303         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53304         this.proxyBottom.show();
53305         return pt;
53306     },
53307
53308     onNodeEnter : function(n, dd, e, data){
53309         if(data.header != n){
53310             this.positionIndicator(data.header, n, e);
53311         }
53312     },
53313
53314     onNodeOver : function(n, dd, e, data){
53315         var result = false;
53316         if(data.header != n){
53317             result = this.positionIndicator(data.header, n, e);
53318         }
53319         if(!result){
53320             this.proxyTop.hide();
53321             this.proxyBottom.hide();
53322         }
53323         return result ? this.dropAllowed : this.dropNotAllowed;
53324     },
53325
53326     onNodeOut : function(n, dd, e, data){
53327         this.proxyTop.hide();
53328         this.proxyBottom.hide();
53329     },
53330
53331     onNodeDrop : function(n, dd, e, data){
53332         var h = data.header;
53333         if(h != n){
53334             var cm = this.grid.colModel;
53335             var x = Roo.lib.Event.getPageX(e);
53336             var r = Roo.lib.Dom.getRegion(n.firstChild);
53337             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53338             var oldIndex = this.view.getCellIndex(h);
53339             var newIndex = this.view.getCellIndex(n);
53340             var locked = cm.isLocked(newIndex);
53341             if(pt == "after"){
53342                 newIndex++;
53343             }
53344             if(oldIndex < newIndex){
53345                 newIndex--;
53346             }
53347             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53348                 return false;
53349             }
53350             cm.setLocked(oldIndex, locked, true);
53351             cm.moveColumn(oldIndex, newIndex);
53352             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53353             return true;
53354         }
53355         return false;
53356     }
53357 });
53358 /*
53359  * Based on:
53360  * Ext JS Library 1.1.1
53361  * Copyright(c) 2006-2007, Ext JS, LLC.
53362  *
53363  * Originally Released Under LGPL - original licence link has changed is not relivant.
53364  *
53365  * Fork - LGPL
53366  * <script type="text/javascript">
53367  */
53368   
53369 /**
53370  * @class Roo.grid.GridView
53371  * @extends Roo.util.Observable
53372  *
53373  * @constructor
53374  * @param {Object} config
53375  */
53376 Roo.grid.GridView = function(config){
53377     Roo.grid.GridView.superclass.constructor.call(this);
53378     this.el = null;
53379
53380     Roo.apply(this, config);
53381 };
53382
53383 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53384
53385     unselectable :  'unselectable="on"',
53386     unselectableCls :  'x-unselectable',
53387     
53388     
53389     rowClass : "x-grid-row",
53390
53391     cellClass : "x-grid-col",
53392
53393     tdClass : "x-grid-td",
53394
53395     hdClass : "x-grid-hd",
53396
53397     splitClass : "x-grid-split",
53398
53399     sortClasses : ["sort-asc", "sort-desc"],
53400
53401     enableMoveAnim : false,
53402
53403     hlColor: "C3DAF9",
53404
53405     dh : Roo.DomHelper,
53406
53407     fly : Roo.Element.fly,
53408
53409     css : Roo.util.CSS,
53410
53411     borderWidth: 1,
53412
53413     splitOffset: 3,
53414
53415     scrollIncrement : 22,
53416
53417     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53418
53419     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53420
53421     bind : function(ds, cm){
53422         if(this.ds){
53423             this.ds.un("load", this.onLoad, this);
53424             this.ds.un("datachanged", this.onDataChange, this);
53425             this.ds.un("add", this.onAdd, this);
53426             this.ds.un("remove", this.onRemove, this);
53427             this.ds.un("update", this.onUpdate, this);
53428             this.ds.un("clear", this.onClear, this);
53429         }
53430         if(ds){
53431             ds.on("load", this.onLoad, this);
53432             ds.on("datachanged", this.onDataChange, this);
53433             ds.on("add", this.onAdd, this);
53434             ds.on("remove", this.onRemove, this);
53435             ds.on("update", this.onUpdate, this);
53436             ds.on("clear", this.onClear, this);
53437         }
53438         this.ds = ds;
53439
53440         if(this.cm){
53441             this.cm.un("widthchange", this.onColWidthChange, this);
53442             this.cm.un("headerchange", this.onHeaderChange, this);
53443             this.cm.un("hiddenchange", this.onHiddenChange, this);
53444             this.cm.un("columnmoved", this.onColumnMove, this);
53445             this.cm.un("columnlockchange", this.onColumnLock, this);
53446         }
53447         if(cm){
53448             this.generateRules(cm);
53449             cm.on("widthchange", this.onColWidthChange, this);
53450             cm.on("headerchange", this.onHeaderChange, this);
53451             cm.on("hiddenchange", this.onHiddenChange, this);
53452             cm.on("columnmoved", this.onColumnMove, this);
53453             cm.on("columnlockchange", this.onColumnLock, this);
53454         }
53455         this.cm = cm;
53456     },
53457
53458     init: function(grid){
53459         Roo.grid.GridView.superclass.init.call(this, grid);
53460
53461         this.bind(grid.dataSource, grid.colModel);
53462
53463         grid.on("headerclick", this.handleHeaderClick, this);
53464
53465         if(grid.trackMouseOver){
53466             grid.on("mouseover", this.onRowOver, this);
53467             grid.on("mouseout", this.onRowOut, this);
53468         }
53469         grid.cancelTextSelection = function(){};
53470         this.gridId = grid.id;
53471
53472         var tpls = this.templates || {};
53473
53474         if(!tpls.master){
53475             tpls.master = new Roo.Template(
53476                '<div class="x-grid" hidefocus="true">',
53477                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53478                   '<div class="x-grid-topbar"></div>',
53479                   '<div class="x-grid-scroller"><div></div></div>',
53480                   '<div class="x-grid-locked">',
53481                       '<div class="x-grid-header">{lockedHeader}</div>',
53482                       '<div class="x-grid-body">{lockedBody}</div>',
53483                   "</div>",
53484                   '<div class="x-grid-viewport">',
53485                       '<div class="x-grid-header">{header}</div>',
53486                       '<div class="x-grid-body">{body}</div>',
53487                   "</div>",
53488                   '<div class="x-grid-bottombar"></div>',
53489                  
53490                   '<div class="x-grid-resize-proxy">&#160;</div>',
53491                "</div>"
53492             );
53493             tpls.master.disableformats = true;
53494         }
53495
53496         if(!tpls.header){
53497             tpls.header = new Roo.Template(
53498                '<table border="0" cellspacing="0" cellpadding="0">',
53499                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53500                "</table>{splits}"
53501             );
53502             tpls.header.disableformats = true;
53503         }
53504         tpls.header.compile();
53505
53506         if(!tpls.hcell){
53507             tpls.hcell = new Roo.Template(
53508                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53509                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53510                 "</div></td>"
53511              );
53512              tpls.hcell.disableFormats = true;
53513         }
53514         tpls.hcell.compile();
53515
53516         if(!tpls.hsplit){
53517             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53518                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53519             tpls.hsplit.disableFormats = true;
53520         }
53521         tpls.hsplit.compile();
53522
53523         if(!tpls.body){
53524             tpls.body = new Roo.Template(
53525                '<table border="0" cellspacing="0" cellpadding="0">',
53526                "<tbody>{rows}</tbody>",
53527                "</table>"
53528             );
53529             tpls.body.disableFormats = true;
53530         }
53531         tpls.body.compile();
53532
53533         if(!tpls.row){
53534             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53535             tpls.row.disableFormats = true;
53536         }
53537         tpls.row.compile();
53538
53539         if(!tpls.cell){
53540             tpls.cell = new Roo.Template(
53541                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53542                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53543                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53544                 "</td>"
53545             );
53546             tpls.cell.disableFormats = true;
53547         }
53548         tpls.cell.compile();
53549
53550         this.templates = tpls;
53551     },
53552
53553     // remap these for backwards compat
53554     onColWidthChange : function(){
53555         this.updateColumns.apply(this, arguments);
53556     },
53557     onHeaderChange : function(){
53558         this.updateHeaders.apply(this, arguments);
53559     }, 
53560     onHiddenChange : function(){
53561         this.handleHiddenChange.apply(this, arguments);
53562     },
53563     onColumnMove : function(){
53564         this.handleColumnMove.apply(this, arguments);
53565     },
53566     onColumnLock : function(){
53567         this.handleLockChange.apply(this, arguments);
53568     },
53569
53570     onDataChange : function(){
53571         this.refresh();
53572         this.updateHeaderSortState();
53573     },
53574
53575     onClear : function(){
53576         this.refresh();
53577     },
53578
53579     onUpdate : function(ds, record){
53580         this.refreshRow(record);
53581     },
53582
53583     refreshRow : function(record){
53584         var ds = this.ds, index;
53585         if(typeof record == 'number'){
53586             index = record;
53587             record = ds.getAt(index);
53588         }else{
53589             index = ds.indexOf(record);
53590         }
53591         this.insertRows(ds, index, index, true);
53592         this.onRemove(ds, record, index+1, true);
53593         this.syncRowHeights(index, index);
53594         this.layout();
53595         this.fireEvent("rowupdated", this, index, record);
53596     },
53597
53598     onAdd : function(ds, records, index){
53599         this.insertRows(ds, index, index + (records.length-1));
53600     },
53601
53602     onRemove : function(ds, record, index, isUpdate){
53603         if(isUpdate !== true){
53604             this.fireEvent("beforerowremoved", this, index, record);
53605         }
53606         var bt = this.getBodyTable(), lt = this.getLockedTable();
53607         if(bt.rows[index]){
53608             bt.firstChild.removeChild(bt.rows[index]);
53609         }
53610         if(lt.rows[index]){
53611             lt.firstChild.removeChild(lt.rows[index]);
53612         }
53613         if(isUpdate !== true){
53614             this.stripeRows(index);
53615             this.syncRowHeights(index, index);
53616             this.layout();
53617             this.fireEvent("rowremoved", this, index, record);
53618         }
53619     },
53620
53621     onLoad : function(){
53622         this.scrollToTop();
53623     },
53624
53625     /**
53626      * Scrolls the grid to the top
53627      */
53628     scrollToTop : function(){
53629         if(this.scroller){
53630             this.scroller.dom.scrollTop = 0;
53631             this.syncScroll();
53632         }
53633     },
53634
53635     /**
53636      * Gets a panel in the header of the grid that can be used for toolbars etc.
53637      * After modifying the contents of this panel a call to grid.autoSize() may be
53638      * required to register any changes in size.
53639      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53640      * @return Roo.Element
53641      */
53642     getHeaderPanel : function(doShow){
53643         if(doShow){
53644             this.headerPanel.show();
53645         }
53646         return this.headerPanel;
53647     },
53648
53649     /**
53650      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53651      * After modifying the contents of this panel a call to grid.autoSize() may be
53652      * required to register any changes in size.
53653      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53654      * @return Roo.Element
53655      */
53656     getFooterPanel : function(doShow){
53657         if(doShow){
53658             this.footerPanel.show();
53659         }
53660         return this.footerPanel;
53661     },
53662
53663     initElements : function(){
53664         var E = Roo.Element;
53665         var el = this.grid.getGridEl().dom.firstChild;
53666         var cs = el.childNodes;
53667
53668         this.el = new E(el);
53669         
53670          this.focusEl = new E(el.firstChild);
53671         this.focusEl.swallowEvent("click", true);
53672         
53673         this.headerPanel = new E(cs[1]);
53674         this.headerPanel.enableDisplayMode("block");
53675
53676         this.scroller = new E(cs[2]);
53677         this.scrollSizer = new E(this.scroller.dom.firstChild);
53678
53679         this.lockedWrap = new E(cs[3]);
53680         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53681         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53682
53683         this.mainWrap = new E(cs[4]);
53684         this.mainHd = new E(this.mainWrap.dom.firstChild);
53685         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53686
53687         this.footerPanel = new E(cs[5]);
53688         this.footerPanel.enableDisplayMode("block");
53689
53690         this.resizeProxy = new E(cs[6]);
53691
53692         this.headerSelector = String.format(
53693            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53694            this.lockedHd.id, this.mainHd.id
53695         );
53696
53697         this.splitterSelector = String.format(
53698            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53699            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53700         );
53701     },
53702     idToCssName : function(s)
53703     {
53704         return s.replace(/[^a-z0-9]+/ig, '-');
53705     },
53706
53707     getHeaderCell : function(index){
53708         return Roo.DomQuery.select(this.headerSelector)[index];
53709     },
53710
53711     getHeaderCellMeasure : function(index){
53712         return this.getHeaderCell(index).firstChild;
53713     },
53714
53715     getHeaderCellText : function(index){
53716         return this.getHeaderCell(index).firstChild.firstChild;
53717     },
53718
53719     getLockedTable : function(){
53720         return this.lockedBody.dom.firstChild;
53721     },
53722
53723     getBodyTable : function(){
53724         return this.mainBody.dom.firstChild;
53725     },
53726
53727     getLockedRow : function(index){
53728         return this.getLockedTable().rows[index];
53729     },
53730
53731     getRow : function(index){
53732         return this.getBodyTable().rows[index];
53733     },
53734
53735     getRowComposite : function(index){
53736         if(!this.rowEl){
53737             this.rowEl = new Roo.CompositeElementLite();
53738         }
53739         var els = [], lrow, mrow;
53740         if(lrow = this.getLockedRow(index)){
53741             els.push(lrow);
53742         }
53743         if(mrow = this.getRow(index)){
53744             els.push(mrow);
53745         }
53746         this.rowEl.elements = els;
53747         return this.rowEl;
53748     },
53749     /**
53750      * Gets the 'td' of the cell
53751      * 
53752      * @param {Integer} rowIndex row to select
53753      * @param {Integer} colIndex column to select
53754      * 
53755      * @return {Object} 
53756      */
53757     getCell : function(rowIndex, colIndex){
53758         var locked = this.cm.getLockedCount();
53759         var source;
53760         if(colIndex < locked){
53761             source = this.lockedBody.dom.firstChild;
53762         }else{
53763             source = this.mainBody.dom.firstChild;
53764             colIndex -= locked;
53765         }
53766         return source.rows[rowIndex].childNodes[colIndex];
53767     },
53768
53769     getCellText : function(rowIndex, colIndex){
53770         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53771     },
53772
53773     getCellBox : function(cell){
53774         var b = this.fly(cell).getBox();
53775         if(Roo.isOpera){ // opera fails to report the Y
53776             b.y = cell.offsetTop + this.mainBody.getY();
53777         }
53778         return b;
53779     },
53780
53781     getCellIndex : function(cell){
53782         var id = String(cell.className).match(this.cellRE);
53783         if(id){
53784             return parseInt(id[1], 10);
53785         }
53786         return 0;
53787     },
53788
53789     findHeaderIndex : function(n){
53790         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53791         return r ? this.getCellIndex(r) : false;
53792     },
53793
53794     findHeaderCell : function(n){
53795         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53796         return r ? r : false;
53797     },
53798
53799     findRowIndex : function(n){
53800         if(!n){
53801             return false;
53802         }
53803         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53804         return r ? r.rowIndex : false;
53805     },
53806
53807     findCellIndex : function(node){
53808         var stop = this.el.dom;
53809         while(node && node != stop){
53810             if(this.findRE.test(node.className)){
53811                 return this.getCellIndex(node);
53812             }
53813             node = node.parentNode;
53814         }
53815         return false;
53816     },
53817
53818     getColumnId : function(index){
53819         return this.cm.getColumnId(index);
53820     },
53821
53822     getSplitters : function()
53823     {
53824         if(this.splitterSelector){
53825            return Roo.DomQuery.select(this.splitterSelector);
53826         }else{
53827             return null;
53828       }
53829     },
53830
53831     getSplitter : function(index){
53832         return this.getSplitters()[index];
53833     },
53834
53835     onRowOver : function(e, t){
53836         var row;
53837         if((row = this.findRowIndex(t)) !== false){
53838             this.getRowComposite(row).addClass("x-grid-row-over");
53839         }
53840     },
53841
53842     onRowOut : function(e, t){
53843         var row;
53844         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53845             this.getRowComposite(row).removeClass("x-grid-row-over");
53846         }
53847     },
53848
53849     renderHeaders : function(){
53850         var cm = this.cm;
53851         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53852         var cb = [], lb = [], sb = [], lsb = [], p = {};
53853         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53854             p.cellId = "x-grid-hd-0-" + i;
53855             p.splitId = "x-grid-csplit-0-" + i;
53856             p.id = cm.getColumnId(i);
53857             p.title = cm.getColumnTooltip(i) || "";
53858             p.value = cm.getColumnHeader(i) || "";
53859             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53860             if(!cm.isLocked(i)){
53861                 cb[cb.length] = ct.apply(p);
53862                 sb[sb.length] = st.apply(p);
53863             }else{
53864                 lb[lb.length] = ct.apply(p);
53865                 lsb[lsb.length] = st.apply(p);
53866             }
53867         }
53868         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53869                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53870     },
53871
53872     updateHeaders : function(){
53873         var html = this.renderHeaders();
53874         this.lockedHd.update(html[0]);
53875         this.mainHd.update(html[1]);
53876     },
53877
53878     /**
53879      * Focuses the specified row.
53880      * @param {Number} row The row index
53881      */
53882     focusRow : function(row)
53883     {
53884         //Roo.log('GridView.focusRow');
53885         var x = this.scroller.dom.scrollLeft;
53886         this.focusCell(row, 0, false);
53887         this.scroller.dom.scrollLeft = x;
53888     },
53889
53890     /**
53891      * Focuses the specified cell.
53892      * @param {Number} row The row index
53893      * @param {Number} col The column index
53894      * @param {Boolean} hscroll false to disable horizontal scrolling
53895      */
53896     focusCell : function(row, col, hscroll)
53897     {
53898         //Roo.log('GridView.focusCell');
53899         var el = this.ensureVisible(row, col, hscroll);
53900         this.focusEl.alignTo(el, "tl-tl");
53901         if(Roo.isGecko){
53902             this.focusEl.focus();
53903         }else{
53904             this.focusEl.focus.defer(1, this.focusEl);
53905         }
53906     },
53907
53908     /**
53909      * Scrolls the specified cell into view
53910      * @param {Number} row The row index
53911      * @param {Number} col The column index
53912      * @param {Boolean} hscroll false to disable horizontal scrolling
53913      */
53914     ensureVisible : function(row, col, hscroll)
53915     {
53916         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53917         //return null; //disable for testing.
53918         if(typeof row != "number"){
53919             row = row.rowIndex;
53920         }
53921         if(row < 0 && row >= this.ds.getCount()){
53922             return  null;
53923         }
53924         col = (col !== undefined ? col : 0);
53925         var cm = this.grid.colModel;
53926         while(cm.isHidden(col)){
53927             col++;
53928         }
53929
53930         var el = this.getCell(row, col);
53931         if(!el){
53932             return null;
53933         }
53934         var c = this.scroller.dom;
53935
53936         var ctop = parseInt(el.offsetTop, 10);
53937         var cleft = parseInt(el.offsetLeft, 10);
53938         var cbot = ctop + el.offsetHeight;
53939         var cright = cleft + el.offsetWidth;
53940         
53941         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53942         var stop = parseInt(c.scrollTop, 10);
53943         var sleft = parseInt(c.scrollLeft, 10);
53944         var sbot = stop + ch;
53945         var sright = sleft + c.clientWidth;
53946         /*
53947         Roo.log('GridView.ensureVisible:' +
53948                 ' ctop:' + ctop +
53949                 ' c.clientHeight:' + c.clientHeight +
53950                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53951                 ' stop:' + stop +
53952                 ' cbot:' + cbot +
53953                 ' sbot:' + sbot +
53954                 ' ch:' + ch  
53955                 );
53956         */
53957         if(ctop < stop){
53958              c.scrollTop = ctop;
53959             //Roo.log("set scrolltop to ctop DISABLE?");
53960         }else if(cbot > sbot){
53961             //Roo.log("set scrolltop to cbot-ch");
53962             c.scrollTop = cbot-ch;
53963         }
53964         
53965         if(hscroll !== false){
53966             if(cleft < sleft){
53967                 c.scrollLeft = cleft;
53968             }else if(cright > sright){
53969                 c.scrollLeft = cright-c.clientWidth;
53970             }
53971         }
53972          
53973         return el;
53974     },
53975
53976     updateColumns : function(){
53977         this.grid.stopEditing();
53978         var cm = this.grid.colModel, colIds = this.getColumnIds();
53979         //var totalWidth = cm.getTotalWidth();
53980         var pos = 0;
53981         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53982             //if(cm.isHidden(i)) continue;
53983             var w = cm.getColumnWidth(i);
53984             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53985             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53986         }
53987         this.updateSplitters();
53988     },
53989
53990     generateRules : function(cm){
53991         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53992         Roo.util.CSS.removeStyleSheet(rulesId);
53993         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53994             var cid = cm.getColumnId(i);
53995             var align = '';
53996             if(cm.config[i].align){
53997                 align = 'text-align:'+cm.config[i].align+';';
53998             }
53999             var hidden = '';
54000             if(cm.isHidden(i)){
54001                 hidden = 'display:none;';
54002             }
54003             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
54004             ruleBuf.push(
54005                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
54006                     this.hdSelector, cid, " {\n", align, width, "}\n",
54007                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
54008                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
54009         }
54010         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54011     },
54012
54013     updateSplitters : function(){
54014         var cm = this.cm, s = this.getSplitters();
54015         if(s){ // splitters not created yet
54016             var pos = 0, locked = true;
54017             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54018                 if(cm.isHidden(i)) continue;
54019                 var w = cm.getColumnWidth(i); // make sure it's a number
54020                 if(!cm.isLocked(i) && locked){
54021                     pos = 0;
54022                     locked = false;
54023                 }
54024                 pos += w;
54025                 s[i].style.left = (pos-this.splitOffset) + "px";
54026             }
54027         }
54028     },
54029
54030     handleHiddenChange : function(colModel, colIndex, hidden){
54031         if(hidden){
54032             this.hideColumn(colIndex);
54033         }else{
54034             this.unhideColumn(colIndex);
54035         }
54036     },
54037
54038     hideColumn : function(colIndex){
54039         var cid = this.getColumnId(colIndex);
54040         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54041         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54042         if(Roo.isSafari){
54043             this.updateHeaders();
54044         }
54045         this.updateSplitters();
54046         this.layout();
54047     },
54048
54049     unhideColumn : function(colIndex){
54050         var cid = this.getColumnId(colIndex);
54051         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54052         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54053
54054         if(Roo.isSafari){
54055             this.updateHeaders();
54056         }
54057         this.updateSplitters();
54058         this.layout();
54059     },
54060
54061     insertRows : function(dm, firstRow, lastRow, isUpdate){
54062         if(firstRow == 0 && lastRow == dm.getCount()-1){
54063             this.refresh();
54064         }else{
54065             if(!isUpdate){
54066                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54067             }
54068             var s = this.getScrollState();
54069             var markup = this.renderRows(firstRow, lastRow);
54070             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54071             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54072             this.restoreScroll(s);
54073             if(!isUpdate){
54074                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54075                 this.syncRowHeights(firstRow, lastRow);
54076                 this.stripeRows(firstRow);
54077                 this.layout();
54078             }
54079         }
54080     },
54081
54082     bufferRows : function(markup, target, index){
54083         var before = null, trows = target.rows, tbody = target.tBodies[0];
54084         if(index < trows.length){
54085             before = trows[index];
54086         }
54087         var b = document.createElement("div");
54088         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54089         var rows = b.firstChild.rows;
54090         for(var i = 0, len = rows.length; i < len; i++){
54091             if(before){
54092                 tbody.insertBefore(rows[0], before);
54093             }else{
54094                 tbody.appendChild(rows[0]);
54095             }
54096         }
54097         b.innerHTML = "";
54098         b = null;
54099     },
54100
54101     deleteRows : function(dm, firstRow, lastRow){
54102         if(dm.getRowCount()<1){
54103             this.fireEvent("beforerefresh", this);
54104             this.mainBody.update("");
54105             this.lockedBody.update("");
54106             this.fireEvent("refresh", this);
54107         }else{
54108             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54109             var bt = this.getBodyTable();
54110             var tbody = bt.firstChild;
54111             var rows = bt.rows;
54112             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54113                 tbody.removeChild(rows[firstRow]);
54114             }
54115             this.stripeRows(firstRow);
54116             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54117         }
54118     },
54119
54120     updateRows : function(dataSource, firstRow, lastRow){
54121         var s = this.getScrollState();
54122         this.refresh();
54123         this.restoreScroll(s);
54124     },
54125
54126     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54127         if(!noRefresh){
54128            this.refresh();
54129         }
54130         this.updateHeaderSortState();
54131     },
54132
54133     getScrollState : function(){
54134         
54135         var sb = this.scroller.dom;
54136         return {left: sb.scrollLeft, top: sb.scrollTop};
54137     },
54138
54139     stripeRows : function(startRow){
54140         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54141             return;
54142         }
54143         startRow = startRow || 0;
54144         var rows = this.getBodyTable().rows;
54145         var lrows = this.getLockedTable().rows;
54146         var cls = ' x-grid-row-alt ';
54147         for(var i = startRow, len = rows.length; i < len; i++){
54148             var row = rows[i], lrow = lrows[i];
54149             var isAlt = ((i+1) % 2 == 0);
54150             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54151             if(isAlt == hasAlt){
54152                 continue;
54153             }
54154             if(isAlt){
54155                 row.className += " x-grid-row-alt";
54156             }else{
54157                 row.className = row.className.replace("x-grid-row-alt", "");
54158             }
54159             if(lrow){
54160                 lrow.className = row.className;
54161             }
54162         }
54163     },
54164
54165     restoreScroll : function(state){
54166         //Roo.log('GridView.restoreScroll');
54167         var sb = this.scroller.dom;
54168         sb.scrollLeft = state.left;
54169         sb.scrollTop = state.top;
54170         this.syncScroll();
54171     },
54172
54173     syncScroll : function(){
54174         //Roo.log('GridView.syncScroll');
54175         var sb = this.scroller.dom;
54176         var sh = this.mainHd.dom;
54177         var bs = this.mainBody.dom;
54178         var lv = this.lockedBody.dom;
54179         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54180         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54181     },
54182
54183     handleScroll : function(e){
54184         this.syncScroll();
54185         var sb = this.scroller.dom;
54186         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54187         e.stopEvent();
54188     },
54189
54190     handleWheel : function(e){
54191         var d = e.getWheelDelta();
54192         this.scroller.dom.scrollTop -= d*22;
54193         // set this here to prevent jumpy scrolling on large tables
54194         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54195         e.stopEvent();
54196     },
54197
54198     renderRows : function(startRow, endRow){
54199         // pull in all the crap needed to render rows
54200         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54201         var colCount = cm.getColumnCount();
54202
54203         if(ds.getCount() < 1){
54204             return ["", ""];
54205         }
54206
54207         // build a map for all the columns
54208         var cs = [];
54209         for(var i = 0; i < colCount; i++){
54210             var name = cm.getDataIndex(i);
54211             cs[i] = {
54212                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54213                 renderer : cm.getRenderer(i),
54214                 id : cm.getColumnId(i),
54215                 locked : cm.isLocked(i)
54216             };
54217         }
54218
54219         startRow = startRow || 0;
54220         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54221
54222         // records to render
54223         var rs = ds.getRange(startRow, endRow);
54224
54225         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54226     },
54227
54228     // As much as I hate to duplicate code, this was branched because FireFox really hates
54229     // [].join("") on strings. The performance difference was substantial enough to
54230     // branch this function
54231     doRender : Roo.isGecko ?
54232             function(cs, rs, ds, startRow, colCount, stripe){
54233                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54234                 // buffers
54235                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54236                 
54237                 var hasListener = this.grid.hasListener('rowclass');
54238                 var rowcfg = {};
54239                 for(var j = 0, len = rs.length; j < len; j++){
54240                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54241                     for(var i = 0; i < colCount; i++){
54242                         c = cs[i];
54243                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54244                         p.id = c.id;
54245                         p.css = p.attr = "";
54246                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54247                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54248                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54249                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54250                         }
54251                         var markup = ct.apply(p);
54252                         if(!c.locked){
54253                             cb+= markup;
54254                         }else{
54255                             lcb+= markup;
54256                         }
54257                     }
54258                     var alt = [];
54259                     if(stripe && ((rowIndex+1) % 2 == 0)){
54260                         alt.push("x-grid-row-alt")
54261                     }
54262                     if(r.dirty){
54263                         alt.push(  " x-grid-dirty-row");
54264                     }
54265                     rp.cells = lcb;
54266                     if(this.getRowClass){
54267                         alt.push(this.getRowClass(r, rowIndex));
54268                     }
54269                     if (hasListener) {
54270                         rowcfg = {
54271                              
54272                             record: r,
54273                             rowIndex : rowIndex,
54274                             rowClass : ''
54275                         };
54276                         this.grid.fireEvent('rowclass', this, rowcfg);
54277                         alt.push(rowcfg.rowClass);
54278                     }
54279                     rp.alt = alt.join(" ");
54280                     lbuf+= rt.apply(rp);
54281                     rp.cells = cb;
54282                     buf+=  rt.apply(rp);
54283                 }
54284                 return [lbuf, buf];
54285             } :
54286             function(cs, rs, ds, startRow, colCount, stripe){
54287                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54288                 // buffers
54289                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54290                 var hasListener = this.grid.hasListener('rowclass');
54291  
54292                 var rowcfg = {};
54293                 for(var j = 0, len = rs.length; j < len; j++){
54294                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54295                     for(var i = 0; i < colCount; i++){
54296                         c = cs[i];
54297                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54298                         p.id = c.id;
54299                         p.css = p.attr = "";
54300                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54301                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54302                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54303                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54304                         }
54305                         
54306                         var markup = ct.apply(p);
54307                         if(!c.locked){
54308                             cb[cb.length] = markup;
54309                         }else{
54310                             lcb[lcb.length] = markup;
54311                         }
54312                     }
54313                     var alt = [];
54314                     if(stripe && ((rowIndex+1) % 2 == 0)){
54315                         alt.push( "x-grid-row-alt");
54316                     }
54317                     if(r.dirty){
54318                         alt.push(" x-grid-dirty-row");
54319                     }
54320                     rp.cells = lcb;
54321                     if(this.getRowClass){
54322                         alt.push( this.getRowClass(r, rowIndex));
54323                     }
54324                     if (hasListener) {
54325                         rowcfg = {
54326                              
54327                             record: r,
54328                             rowIndex : rowIndex,
54329                             rowClass : ''
54330                         };
54331                         this.grid.fireEvent('rowclass', this, rowcfg);
54332                         alt.push(rowcfg.rowClass);
54333                     }
54334                     rp.alt = alt.join(" ");
54335                     rp.cells = lcb.join("");
54336                     lbuf[lbuf.length] = rt.apply(rp);
54337                     rp.cells = cb.join("");
54338                     buf[buf.length] =  rt.apply(rp);
54339                 }
54340                 return [lbuf.join(""), buf.join("")];
54341             },
54342
54343     renderBody : function(){
54344         var markup = this.renderRows();
54345         var bt = this.templates.body;
54346         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54347     },
54348
54349     /**
54350      * Refreshes the grid
54351      * @param {Boolean} headersToo
54352      */
54353     refresh : function(headersToo){
54354         this.fireEvent("beforerefresh", this);
54355         this.grid.stopEditing();
54356         var result = this.renderBody();
54357         this.lockedBody.update(result[0]);
54358         this.mainBody.update(result[1]);
54359         if(headersToo === true){
54360             this.updateHeaders();
54361             this.updateColumns();
54362             this.updateSplitters();
54363             this.updateHeaderSortState();
54364         }
54365         this.syncRowHeights();
54366         this.layout();
54367         this.fireEvent("refresh", this);
54368     },
54369
54370     handleColumnMove : function(cm, oldIndex, newIndex){
54371         this.indexMap = null;
54372         var s = this.getScrollState();
54373         this.refresh(true);
54374         this.restoreScroll(s);
54375         this.afterMove(newIndex);
54376     },
54377
54378     afterMove : function(colIndex){
54379         if(this.enableMoveAnim && Roo.enableFx){
54380             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54381         }
54382         // if multisort - fix sortOrder, and reload..
54383         if (this.grid.dataSource.multiSort) {
54384             // the we can call sort again..
54385             var dm = this.grid.dataSource;
54386             var cm = this.grid.colModel;
54387             var so = [];
54388             for(var i = 0; i < cm.config.length; i++ ) {
54389                 
54390                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54391                     continue; // dont' bother, it's not in sort list or being set.
54392                 }
54393                 
54394                 so.push(cm.config[i].dataIndex);
54395             };
54396             dm.sortOrder = so;
54397             dm.load(dm.lastOptions);
54398             
54399             
54400         }
54401         
54402     },
54403
54404     updateCell : function(dm, rowIndex, dataIndex){
54405         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54406         if(typeof colIndex == "undefined"){ // not present in grid
54407             return;
54408         }
54409         var cm = this.grid.colModel;
54410         var cell = this.getCell(rowIndex, colIndex);
54411         var cellText = this.getCellText(rowIndex, colIndex);
54412
54413         var p = {
54414             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54415             id : cm.getColumnId(colIndex),
54416             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54417         };
54418         var renderer = cm.getRenderer(colIndex);
54419         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54420         if(typeof val == "undefined" || val === "") val = "&#160;";
54421         cellText.innerHTML = val;
54422         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54423         this.syncRowHeights(rowIndex, rowIndex);
54424     },
54425
54426     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54427         var maxWidth = 0;
54428         if(this.grid.autoSizeHeaders){
54429             var h = this.getHeaderCellMeasure(colIndex);
54430             maxWidth = Math.max(maxWidth, h.scrollWidth);
54431         }
54432         var tb, index;
54433         if(this.cm.isLocked(colIndex)){
54434             tb = this.getLockedTable();
54435             index = colIndex;
54436         }else{
54437             tb = this.getBodyTable();
54438             index = colIndex - this.cm.getLockedCount();
54439         }
54440         if(tb && tb.rows){
54441             var rows = tb.rows;
54442             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54443             for(var i = 0; i < stopIndex; i++){
54444                 var cell = rows[i].childNodes[index].firstChild;
54445                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54446             }
54447         }
54448         return maxWidth + /*margin for error in IE*/ 5;
54449     },
54450     /**
54451      * Autofit a column to its content.
54452      * @param {Number} colIndex
54453      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54454      */
54455      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54456          if(this.cm.isHidden(colIndex)){
54457              return; // can't calc a hidden column
54458          }
54459         if(forceMinSize){
54460             var cid = this.cm.getColumnId(colIndex);
54461             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54462            if(this.grid.autoSizeHeaders){
54463                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54464            }
54465         }
54466         var newWidth = this.calcColumnWidth(colIndex);
54467         this.cm.setColumnWidth(colIndex,
54468             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54469         if(!suppressEvent){
54470             this.grid.fireEvent("columnresize", colIndex, newWidth);
54471         }
54472     },
54473
54474     /**
54475      * Autofits all columns to their content and then expands to fit any extra space in the grid
54476      */
54477      autoSizeColumns : function(){
54478         var cm = this.grid.colModel;
54479         var colCount = cm.getColumnCount();
54480         for(var i = 0; i < colCount; i++){
54481             this.autoSizeColumn(i, true, true);
54482         }
54483         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54484             this.fitColumns();
54485         }else{
54486             this.updateColumns();
54487             this.layout();
54488         }
54489     },
54490
54491     /**
54492      * Autofits all columns to the grid's width proportionate with their current size
54493      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54494      */
54495     fitColumns : function(reserveScrollSpace){
54496         var cm = this.grid.colModel;
54497         var colCount = cm.getColumnCount();
54498         var cols = [];
54499         var width = 0;
54500         var i, w;
54501         for (i = 0; i < colCount; i++){
54502             if(!cm.isHidden(i) && !cm.isFixed(i)){
54503                 w = cm.getColumnWidth(i);
54504                 cols.push(i);
54505                 cols.push(w);
54506                 width += w;
54507             }
54508         }
54509         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54510         if(reserveScrollSpace){
54511             avail -= 17;
54512         }
54513         var frac = (avail - cm.getTotalWidth())/width;
54514         while (cols.length){
54515             w = cols.pop();
54516             i = cols.pop();
54517             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54518         }
54519         this.updateColumns();
54520         this.layout();
54521     },
54522
54523     onRowSelect : function(rowIndex){
54524         var row = this.getRowComposite(rowIndex);
54525         row.addClass("x-grid-row-selected");
54526     },
54527
54528     onRowDeselect : function(rowIndex){
54529         var row = this.getRowComposite(rowIndex);
54530         row.removeClass("x-grid-row-selected");
54531     },
54532
54533     onCellSelect : function(row, col){
54534         var cell = this.getCell(row, col);
54535         if(cell){
54536             Roo.fly(cell).addClass("x-grid-cell-selected");
54537         }
54538     },
54539
54540     onCellDeselect : function(row, col){
54541         var cell = this.getCell(row, col);
54542         if(cell){
54543             Roo.fly(cell).removeClass("x-grid-cell-selected");
54544         }
54545     },
54546
54547     updateHeaderSortState : function(){
54548         
54549         // sort state can be single { field: xxx, direction : yyy}
54550         // or   { xxx=>ASC , yyy : DESC ..... }
54551         
54552         var mstate = {};
54553         if (!this.ds.multiSort) { 
54554             var state = this.ds.getSortState();
54555             if(!state){
54556                 return;
54557             }
54558             mstate[state.field] = state.direction;
54559             // FIXME... - this is not used here.. but might be elsewhere..
54560             this.sortState = state;
54561             
54562         } else {
54563             mstate = this.ds.sortToggle;
54564         }
54565         //remove existing sort classes..
54566         
54567         var sc = this.sortClasses;
54568         var hds = this.el.select(this.headerSelector).removeClass(sc);
54569         
54570         for(var f in mstate) {
54571         
54572             var sortColumn = this.cm.findColumnIndex(f);
54573             
54574             if(sortColumn != -1){
54575                 var sortDir = mstate[f];        
54576                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54577             }
54578         }
54579         
54580          
54581         
54582     },
54583
54584
54585     handleHeaderClick : function(g, index,e){
54586         
54587         Roo.log("header click");
54588         
54589         if (Roo.isTouch) {
54590             // touch events on header are handled by context
54591             this.handleHdCtx(g,index,e);
54592             return;
54593         }
54594         
54595         
54596         if(this.headersDisabled){
54597             return;
54598         }
54599         var dm = g.dataSource, cm = g.colModel;
54600         if(!cm.isSortable(index)){
54601             return;
54602         }
54603         g.stopEditing();
54604         
54605         if (dm.multiSort) {
54606             // update the sortOrder
54607             var so = [];
54608             for(var i = 0; i < cm.config.length; i++ ) {
54609                 
54610                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54611                     continue; // dont' bother, it's not in sort list or being set.
54612                 }
54613                 
54614                 so.push(cm.config[i].dataIndex);
54615             };
54616             dm.sortOrder = so;
54617         }
54618         
54619         
54620         dm.sort(cm.getDataIndex(index));
54621     },
54622
54623
54624     destroy : function(){
54625         if(this.colMenu){
54626             this.colMenu.removeAll();
54627             Roo.menu.MenuMgr.unregister(this.colMenu);
54628             this.colMenu.getEl().remove();
54629             delete this.colMenu;
54630         }
54631         if(this.hmenu){
54632             this.hmenu.removeAll();
54633             Roo.menu.MenuMgr.unregister(this.hmenu);
54634             this.hmenu.getEl().remove();
54635             delete this.hmenu;
54636         }
54637         if(this.grid.enableColumnMove){
54638             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54639             if(dds){
54640                 for(var dd in dds){
54641                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54642                         var elid = dds[dd].dragElId;
54643                         dds[dd].unreg();
54644                         Roo.get(elid).remove();
54645                     } else if(dds[dd].config.isTarget){
54646                         dds[dd].proxyTop.remove();
54647                         dds[dd].proxyBottom.remove();
54648                         dds[dd].unreg();
54649                     }
54650                     if(Roo.dd.DDM.locationCache[dd]){
54651                         delete Roo.dd.DDM.locationCache[dd];
54652                     }
54653                 }
54654                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54655             }
54656         }
54657         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54658         this.bind(null, null);
54659         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54660     },
54661
54662     handleLockChange : function(){
54663         this.refresh(true);
54664     },
54665
54666     onDenyColumnLock : function(){
54667
54668     },
54669
54670     onDenyColumnHide : function(){
54671
54672     },
54673
54674     handleHdMenuClick : function(item){
54675         var index = this.hdCtxIndex;
54676         var cm = this.cm, ds = this.ds;
54677         switch(item.id){
54678             case "asc":
54679                 ds.sort(cm.getDataIndex(index), "ASC");
54680                 break;
54681             case "desc":
54682                 ds.sort(cm.getDataIndex(index), "DESC");
54683                 break;
54684             case "lock":
54685                 var lc = cm.getLockedCount();
54686                 if(cm.getColumnCount(true) <= lc+1){
54687                     this.onDenyColumnLock();
54688                     return;
54689                 }
54690                 if(lc != index){
54691                     cm.setLocked(index, true, true);
54692                     cm.moveColumn(index, lc);
54693                     this.grid.fireEvent("columnmove", index, lc);
54694                 }else{
54695                     cm.setLocked(index, true);
54696                 }
54697             break;
54698             case "unlock":
54699                 var lc = cm.getLockedCount();
54700                 if((lc-1) != index){
54701                     cm.setLocked(index, false, true);
54702                     cm.moveColumn(index, lc-1);
54703                     this.grid.fireEvent("columnmove", index, lc-1);
54704                 }else{
54705                     cm.setLocked(index, false);
54706                 }
54707             break;
54708             case 'wider': // used to expand cols on touch..
54709             case 'narrow':
54710                 var cw = cm.getColumnWidth(index);
54711                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54712                 cw = Math.max(0, cw);
54713                 cw = Math.min(cw,4000);
54714                 cm.setColumnWidth(index, cw);
54715                 break;
54716                 
54717             default:
54718                 index = cm.getIndexById(item.id.substr(4));
54719                 if(index != -1){
54720                     if(item.checked && cm.getColumnCount(true) <= 1){
54721                         this.onDenyColumnHide();
54722                         return false;
54723                     }
54724                     cm.setHidden(index, item.checked);
54725                 }
54726         }
54727         return true;
54728     },
54729
54730     beforeColMenuShow : function(){
54731         var cm = this.cm,  colCount = cm.getColumnCount();
54732         this.colMenu.removeAll();
54733         for(var i = 0; i < colCount; i++){
54734             this.colMenu.add(new Roo.menu.CheckItem({
54735                 id: "col-"+cm.getColumnId(i),
54736                 text: cm.getColumnHeader(i),
54737                 checked: !cm.isHidden(i),
54738                 hideOnClick:false
54739             }));
54740         }
54741     },
54742
54743     handleHdCtx : function(g, index, e){
54744         e.stopEvent();
54745         var hd = this.getHeaderCell(index);
54746         this.hdCtxIndex = index;
54747         var ms = this.hmenu.items, cm = this.cm;
54748         ms.get("asc").setDisabled(!cm.isSortable(index));
54749         ms.get("desc").setDisabled(!cm.isSortable(index));
54750         if(this.grid.enableColLock !== false){
54751             ms.get("lock").setDisabled(cm.isLocked(index));
54752             ms.get("unlock").setDisabled(!cm.isLocked(index));
54753         }
54754         this.hmenu.show(hd, "tl-bl");
54755     },
54756
54757     handleHdOver : function(e){
54758         var hd = this.findHeaderCell(e.getTarget());
54759         if(hd && !this.headersDisabled){
54760             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54761                this.fly(hd).addClass("x-grid-hd-over");
54762             }
54763         }
54764     },
54765
54766     handleHdOut : function(e){
54767         var hd = this.findHeaderCell(e.getTarget());
54768         if(hd){
54769             this.fly(hd).removeClass("x-grid-hd-over");
54770         }
54771     },
54772
54773     handleSplitDblClick : function(e, t){
54774         var i = this.getCellIndex(t);
54775         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54776             this.autoSizeColumn(i, true);
54777             this.layout();
54778         }
54779     },
54780
54781     render : function(){
54782
54783         var cm = this.cm;
54784         var colCount = cm.getColumnCount();
54785
54786         if(this.grid.monitorWindowResize === true){
54787             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54788         }
54789         var header = this.renderHeaders();
54790         var body = this.templates.body.apply({rows:""});
54791         var html = this.templates.master.apply({
54792             lockedBody: body,
54793             body: body,
54794             lockedHeader: header[0],
54795             header: header[1]
54796         });
54797
54798         //this.updateColumns();
54799
54800         this.grid.getGridEl().dom.innerHTML = html;
54801
54802         this.initElements();
54803         
54804         // a kludge to fix the random scolling effect in webkit
54805         this.el.on("scroll", function() {
54806             this.el.dom.scrollTop=0; // hopefully not recursive..
54807         },this);
54808
54809         this.scroller.on("scroll", this.handleScroll, this);
54810         this.lockedBody.on("mousewheel", this.handleWheel, this);
54811         this.mainBody.on("mousewheel", this.handleWheel, this);
54812
54813         this.mainHd.on("mouseover", this.handleHdOver, this);
54814         this.mainHd.on("mouseout", this.handleHdOut, this);
54815         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54816                 {delegate: "."+this.splitClass});
54817
54818         this.lockedHd.on("mouseover", this.handleHdOver, this);
54819         this.lockedHd.on("mouseout", this.handleHdOut, this);
54820         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54821                 {delegate: "."+this.splitClass});
54822
54823         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54824             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54825         }
54826
54827         this.updateSplitters();
54828
54829         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54830             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54831             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54832         }
54833
54834         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54835             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54836             this.hmenu.add(
54837                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54838                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54839             );
54840             if(this.grid.enableColLock !== false){
54841                 this.hmenu.add('-',
54842                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54843                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54844                 );
54845             }
54846             if (Roo.isTouch) {
54847                  this.hmenu.add('-',
54848                     {id:"wider", text: this.columnsWiderText},
54849                     {id:"narrow", text: this.columnsNarrowText }
54850                 );
54851                 
54852                  
54853             }
54854             
54855             if(this.grid.enableColumnHide !== false){
54856
54857                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54858                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54859                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54860
54861                 this.hmenu.add('-',
54862                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54863                 );
54864             }
54865             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54866
54867             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54868         }
54869
54870         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54871             this.dd = new Roo.grid.GridDragZone(this.grid, {
54872                 ddGroup : this.grid.ddGroup || 'GridDD'
54873             });
54874             
54875         }
54876
54877         /*
54878         for(var i = 0; i < colCount; i++){
54879             if(cm.isHidden(i)){
54880                 this.hideColumn(i);
54881             }
54882             if(cm.config[i].align){
54883                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54884                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54885             }
54886         }*/
54887         
54888         this.updateHeaderSortState();
54889
54890         this.beforeInitialResize();
54891         this.layout(true);
54892
54893         // two part rendering gives faster view to the user
54894         this.renderPhase2.defer(1, this);
54895     },
54896
54897     renderPhase2 : function(){
54898         // render the rows now
54899         this.refresh();
54900         if(this.grid.autoSizeColumns){
54901             this.autoSizeColumns();
54902         }
54903     },
54904
54905     beforeInitialResize : function(){
54906
54907     },
54908
54909     onColumnSplitterMoved : function(i, w){
54910         this.userResized = true;
54911         var cm = this.grid.colModel;
54912         cm.setColumnWidth(i, w, true);
54913         var cid = cm.getColumnId(i);
54914         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54915         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54916         this.updateSplitters();
54917         this.layout();
54918         this.grid.fireEvent("columnresize", i, w);
54919     },
54920
54921     syncRowHeights : function(startIndex, endIndex){
54922         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54923             startIndex = startIndex || 0;
54924             var mrows = this.getBodyTable().rows;
54925             var lrows = this.getLockedTable().rows;
54926             var len = mrows.length-1;
54927             endIndex = Math.min(endIndex || len, len);
54928             for(var i = startIndex; i <= endIndex; i++){
54929                 var m = mrows[i], l = lrows[i];
54930                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54931                 m.style.height = l.style.height = h + "px";
54932             }
54933         }
54934     },
54935
54936     layout : function(initialRender, is2ndPass){
54937         var g = this.grid;
54938         var auto = g.autoHeight;
54939         var scrollOffset = 16;
54940         var c = g.getGridEl(), cm = this.cm,
54941                 expandCol = g.autoExpandColumn,
54942                 gv = this;
54943         //c.beginMeasure();
54944
54945         if(!c.dom.offsetWidth){ // display:none?
54946             if(initialRender){
54947                 this.lockedWrap.show();
54948                 this.mainWrap.show();
54949             }
54950             return;
54951         }
54952
54953         var hasLock = this.cm.isLocked(0);
54954
54955         var tbh = this.headerPanel.getHeight();
54956         var bbh = this.footerPanel.getHeight();
54957
54958         if(auto){
54959             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54960             var newHeight = ch + c.getBorderWidth("tb");
54961             if(g.maxHeight){
54962                 newHeight = Math.min(g.maxHeight, newHeight);
54963             }
54964             c.setHeight(newHeight);
54965         }
54966
54967         if(g.autoWidth){
54968             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54969         }
54970
54971         var s = this.scroller;
54972
54973         var csize = c.getSize(true);
54974
54975         this.el.setSize(csize.width, csize.height);
54976
54977         this.headerPanel.setWidth(csize.width);
54978         this.footerPanel.setWidth(csize.width);
54979
54980         var hdHeight = this.mainHd.getHeight();
54981         var vw = csize.width;
54982         var vh = csize.height - (tbh + bbh);
54983
54984         s.setSize(vw, vh);
54985
54986         var bt = this.getBodyTable();
54987         var ltWidth = hasLock ?
54988                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54989
54990         var scrollHeight = bt.offsetHeight;
54991         var scrollWidth = ltWidth + bt.offsetWidth;
54992         var vscroll = false, hscroll = false;
54993
54994         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54995
54996         var lw = this.lockedWrap, mw = this.mainWrap;
54997         var lb = this.lockedBody, mb = this.mainBody;
54998
54999         setTimeout(function(){
55000             var t = s.dom.offsetTop;
55001             var w = s.dom.clientWidth,
55002                 h = s.dom.clientHeight;
55003
55004             lw.setTop(t);
55005             lw.setSize(ltWidth, h);
55006
55007             mw.setLeftTop(ltWidth, t);
55008             mw.setSize(w-ltWidth, h);
55009
55010             lb.setHeight(h-hdHeight);
55011             mb.setHeight(h-hdHeight);
55012
55013             if(is2ndPass !== true && !gv.userResized && expandCol){
55014                 // high speed resize without full column calculation
55015                 
55016                 var ci = cm.getIndexById(expandCol);
55017                 if (ci < 0) {
55018                     ci = cm.findColumnIndex(expandCol);
55019                 }
55020                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55021                 var expandId = cm.getColumnId(ci);
55022                 var  tw = cm.getTotalWidth(false);
55023                 var currentWidth = cm.getColumnWidth(ci);
55024                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55025                 if(currentWidth != cw){
55026                     cm.setColumnWidth(ci, cw, true);
55027                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55028                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55029                     gv.updateSplitters();
55030                     gv.layout(false, true);
55031                 }
55032             }
55033
55034             if(initialRender){
55035                 lw.show();
55036                 mw.show();
55037             }
55038             //c.endMeasure();
55039         }, 10);
55040     },
55041
55042     onWindowResize : function(){
55043         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55044             return;
55045         }
55046         this.layout();
55047     },
55048
55049     appendFooter : function(parentEl){
55050         return null;
55051     },
55052
55053     sortAscText : "Sort Ascending",
55054     sortDescText : "Sort Descending",
55055     lockText : "Lock Column",
55056     unlockText : "Unlock Column",
55057     columnsText : "Columns",
55058  
55059     columnsWiderText : "Wider",
55060     columnsNarrowText : "Thinner"
55061 });
55062
55063
55064 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55065     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55066     this.proxy.el.addClass('x-grid3-col-dd');
55067 };
55068
55069 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55070     handleMouseDown : function(e){
55071
55072     },
55073
55074     callHandleMouseDown : function(e){
55075         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55076     }
55077 });
55078 /*
55079  * Based on:
55080  * Ext JS Library 1.1.1
55081  * Copyright(c) 2006-2007, Ext JS, LLC.
55082  *
55083  * Originally Released Under LGPL - original licence link has changed is not relivant.
55084  *
55085  * Fork - LGPL
55086  * <script type="text/javascript">
55087  */
55088  
55089 // private
55090 // This is a support class used internally by the Grid components
55091 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55092     this.grid = grid;
55093     this.view = grid.getView();
55094     this.proxy = this.view.resizeProxy;
55095     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55096         "gridSplitters" + this.grid.getGridEl().id, {
55097         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55098     });
55099     this.setHandleElId(Roo.id(hd));
55100     this.setOuterHandleElId(Roo.id(hd2));
55101     this.scroll = false;
55102 };
55103 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55104     fly: Roo.Element.fly,
55105
55106     b4StartDrag : function(x, y){
55107         this.view.headersDisabled = true;
55108         this.proxy.setHeight(this.view.mainWrap.getHeight());
55109         var w = this.cm.getColumnWidth(this.cellIndex);
55110         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55111         this.resetConstraints();
55112         this.setXConstraint(minw, 1000);
55113         this.setYConstraint(0, 0);
55114         this.minX = x - minw;
55115         this.maxX = x + 1000;
55116         this.startPos = x;
55117         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55118     },
55119
55120
55121     handleMouseDown : function(e){
55122         ev = Roo.EventObject.setEvent(e);
55123         var t = this.fly(ev.getTarget());
55124         if(t.hasClass("x-grid-split")){
55125             this.cellIndex = this.view.getCellIndex(t.dom);
55126             this.split = t.dom;
55127             this.cm = this.grid.colModel;
55128             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55129                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55130             }
55131         }
55132     },
55133
55134     endDrag : function(e){
55135         this.view.headersDisabled = false;
55136         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55137         var diff = endX - this.startPos;
55138         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55139     },
55140
55141     autoOffset : function(){
55142         this.setDelta(0,0);
55143     }
55144 });/*
55145  * Based on:
55146  * Ext JS Library 1.1.1
55147  * Copyright(c) 2006-2007, Ext JS, LLC.
55148  *
55149  * Originally Released Under LGPL - original licence link has changed is not relivant.
55150  *
55151  * Fork - LGPL
55152  * <script type="text/javascript">
55153  */
55154  
55155 // private
55156 // This is a support class used internally by the Grid components
55157 Roo.grid.GridDragZone = function(grid, config){
55158     this.view = grid.getView();
55159     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55160     if(this.view.lockedBody){
55161         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55162         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55163     }
55164     this.scroll = false;
55165     this.grid = grid;
55166     this.ddel = document.createElement('div');
55167     this.ddel.className = 'x-grid-dd-wrap';
55168 };
55169
55170 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55171     ddGroup : "GridDD",
55172
55173     getDragData : function(e){
55174         var t = Roo.lib.Event.getTarget(e);
55175         var rowIndex = this.view.findRowIndex(t);
55176         var sm = this.grid.selModel;
55177             
55178         //Roo.log(rowIndex);
55179         
55180         if (sm.getSelectedCell) {
55181             // cell selection..
55182             if (!sm.getSelectedCell()) {
55183                 return false;
55184             }
55185             if (rowIndex != sm.getSelectedCell()[0]) {
55186                 return false;
55187             }
55188         
55189         }
55190         
55191         if(rowIndex !== false){
55192             
55193             // if editorgrid.. 
55194             
55195             
55196             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55197                
55198             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55199               //  
55200             //}
55201             if (e.hasModifier()){
55202                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55203             }
55204             
55205             Roo.log("getDragData");
55206             
55207             return {
55208                 grid: this.grid,
55209                 ddel: this.ddel,
55210                 rowIndex: rowIndex,
55211                 selections:sm.getSelections ? sm.getSelections() : (
55212                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55213                 )
55214             };
55215         }
55216         return false;
55217     },
55218
55219     onInitDrag : function(e){
55220         var data = this.dragData;
55221         this.ddel.innerHTML = this.grid.getDragDropText();
55222         this.proxy.update(this.ddel);
55223         // fire start drag?
55224     },
55225
55226     afterRepair : function(){
55227         this.dragging = false;
55228     },
55229
55230     getRepairXY : function(e, data){
55231         return false;
55232     },
55233
55234     onEndDrag : function(data, e){
55235         // fire end drag?
55236     },
55237
55238     onValidDrop : function(dd, e, id){
55239         // fire drag drop?
55240         this.hideProxy();
55241     },
55242
55243     beforeInvalidDrop : function(e, id){
55244
55245     }
55246 });/*
55247  * Based on:
55248  * Ext JS Library 1.1.1
55249  * Copyright(c) 2006-2007, Ext JS, LLC.
55250  *
55251  * Originally Released Under LGPL - original licence link has changed is not relivant.
55252  *
55253  * Fork - LGPL
55254  * <script type="text/javascript">
55255  */
55256  
55257
55258 /**
55259  * @class Roo.grid.ColumnModel
55260  * @extends Roo.util.Observable
55261  * This is the default implementation of a ColumnModel used by the Grid. It defines
55262  * the columns in the grid.
55263  * <br>Usage:<br>
55264  <pre><code>
55265  var colModel = new Roo.grid.ColumnModel([
55266         {header: "Ticker", width: 60, sortable: true, locked: true},
55267         {header: "Company Name", width: 150, sortable: true},
55268         {header: "Market Cap.", width: 100, sortable: true},
55269         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55270         {header: "Employees", width: 100, sortable: true, resizable: false}
55271  ]);
55272  </code></pre>
55273  * <p>
55274  
55275  * The config options listed for this class are options which may appear in each
55276  * individual column definition.
55277  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55278  * @constructor
55279  * @param {Object} config An Array of column config objects. See this class's
55280  * config objects for details.
55281 */
55282 Roo.grid.ColumnModel = function(config){
55283         /**
55284      * The config passed into the constructor
55285      */
55286     this.config = config;
55287     this.lookup = {};
55288
55289     // if no id, create one
55290     // if the column does not have a dataIndex mapping,
55291     // map it to the order it is in the config
55292     for(var i = 0, len = config.length; i < len; i++){
55293         var c = config[i];
55294         if(typeof c.dataIndex == "undefined"){
55295             c.dataIndex = i;
55296         }
55297         if(typeof c.renderer == "string"){
55298             c.renderer = Roo.util.Format[c.renderer];
55299         }
55300         if(typeof c.id == "undefined"){
55301             c.id = Roo.id();
55302         }
55303         if(c.editor && c.editor.xtype){
55304             c.editor  = Roo.factory(c.editor, Roo.grid);
55305         }
55306         if(c.editor && c.editor.isFormField){
55307             c.editor = new Roo.grid.GridEditor(c.editor);
55308         }
55309         this.lookup[c.id] = c;
55310     }
55311
55312     /**
55313      * The width of columns which have no width specified (defaults to 100)
55314      * @type Number
55315      */
55316     this.defaultWidth = 100;
55317
55318     /**
55319      * Default sortable of columns which have no sortable specified (defaults to false)
55320      * @type Boolean
55321      */
55322     this.defaultSortable = false;
55323
55324     this.addEvents({
55325         /**
55326              * @event widthchange
55327              * Fires when the width of a column changes.
55328              * @param {ColumnModel} this
55329              * @param {Number} columnIndex The column index
55330              * @param {Number} newWidth The new width
55331              */
55332             "widthchange": true,
55333         /**
55334              * @event headerchange
55335              * Fires when the text of a header changes.
55336              * @param {ColumnModel} this
55337              * @param {Number} columnIndex The column index
55338              * @param {Number} newText The new header text
55339              */
55340             "headerchange": true,
55341         /**
55342              * @event hiddenchange
55343              * Fires when a column is hidden or "unhidden".
55344              * @param {ColumnModel} this
55345              * @param {Number} columnIndex The column index
55346              * @param {Boolean} hidden true if hidden, false otherwise
55347              */
55348             "hiddenchange": true,
55349             /**
55350          * @event columnmoved
55351          * Fires when a column is moved.
55352          * @param {ColumnModel} this
55353          * @param {Number} oldIndex
55354          * @param {Number} newIndex
55355          */
55356         "columnmoved" : true,
55357         /**
55358          * @event columlockchange
55359          * Fires when a column's locked state is changed
55360          * @param {ColumnModel} this
55361          * @param {Number} colIndex
55362          * @param {Boolean} locked true if locked
55363          */
55364         "columnlockchange" : true
55365     });
55366     Roo.grid.ColumnModel.superclass.constructor.call(this);
55367 };
55368 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55369     /**
55370      * @cfg {String} header The header text to display in the Grid view.
55371      */
55372     /**
55373      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55374      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55375      * specified, the column's index is used as an index into the Record's data Array.
55376      */
55377     /**
55378      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55379      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55380      */
55381     /**
55382      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55383      * Defaults to the value of the {@link #defaultSortable} property.
55384      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55385      */
55386     /**
55387      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55388      */
55389     /**
55390      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55391      */
55392     /**
55393      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55394      */
55395     /**
55396      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55397      */
55398     /**
55399      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55400      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55401      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55402      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55403      */
55404        /**
55405      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55406      */
55407     /**
55408      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55409      */
55410     /**
55411      * @cfg {String} cursor (Optional)
55412      */
55413     /**
55414      * @cfg {String} tooltip (Optional)
55415      */
55416     /**
55417      * @cfg {Number} xs (Optional)
55418      */
55419     /**
55420      * @cfg {Number} sm (Optional)
55421      */
55422     /**
55423      * @cfg {Number} md (Optional)
55424      */
55425     /**
55426      * @cfg {Number} lg (Optional)
55427      */
55428     /**
55429      * Returns the id of the column at the specified index.
55430      * @param {Number} index The column index
55431      * @return {String} the id
55432      */
55433     getColumnId : function(index){
55434         return this.config[index].id;
55435     },
55436
55437     /**
55438      * Returns the column for a specified id.
55439      * @param {String} id The column id
55440      * @return {Object} the column
55441      */
55442     getColumnById : function(id){
55443         return this.lookup[id];
55444     },
55445
55446     
55447     /**
55448      * Returns the column for a specified dataIndex.
55449      * @param {String} dataIndex The column dataIndex
55450      * @return {Object|Boolean} the column or false if not found
55451      */
55452     getColumnByDataIndex: function(dataIndex){
55453         var index = this.findColumnIndex(dataIndex);
55454         return index > -1 ? this.config[index] : false;
55455     },
55456     
55457     /**
55458      * Returns the index for a specified column id.
55459      * @param {String} id The column id
55460      * @return {Number} the index, or -1 if not found
55461      */
55462     getIndexById : function(id){
55463         for(var i = 0, len = this.config.length; i < len; i++){
55464             if(this.config[i].id == id){
55465                 return i;
55466             }
55467         }
55468         return -1;
55469     },
55470     
55471     /**
55472      * Returns the index for a specified column dataIndex.
55473      * @param {String} dataIndex The column dataIndex
55474      * @return {Number} the index, or -1 if not found
55475      */
55476     
55477     findColumnIndex : function(dataIndex){
55478         for(var i = 0, len = this.config.length; i < len; i++){
55479             if(this.config[i].dataIndex == dataIndex){
55480                 return i;
55481             }
55482         }
55483         return -1;
55484     },
55485     
55486     
55487     moveColumn : function(oldIndex, newIndex){
55488         var c = this.config[oldIndex];
55489         this.config.splice(oldIndex, 1);
55490         this.config.splice(newIndex, 0, c);
55491         this.dataMap = null;
55492         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55493     },
55494
55495     isLocked : function(colIndex){
55496         return this.config[colIndex].locked === true;
55497     },
55498
55499     setLocked : function(colIndex, value, suppressEvent){
55500         if(this.isLocked(colIndex) == value){
55501             return;
55502         }
55503         this.config[colIndex].locked = value;
55504         if(!suppressEvent){
55505             this.fireEvent("columnlockchange", this, colIndex, value);
55506         }
55507     },
55508
55509     getTotalLockedWidth : function(){
55510         var totalWidth = 0;
55511         for(var i = 0; i < this.config.length; i++){
55512             if(this.isLocked(i) && !this.isHidden(i)){
55513                 this.totalWidth += this.getColumnWidth(i);
55514             }
55515         }
55516         return totalWidth;
55517     },
55518
55519     getLockedCount : function(){
55520         for(var i = 0, len = this.config.length; i < len; i++){
55521             if(!this.isLocked(i)){
55522                 return i;
55523             }
55524         }
55525     },
55526
55527     /**
55528      * Returns the number of columns.
55529      * @return {Number}
55530      */
55531     getColumnCount : function(visibleOnly){
55532         if(visibleOnly === true){
55533             var c = 0;
55534             for(var i = 0, len = this.config.length; i < len; i++){
55535                 if(!this.isHidden(i)){
55536                     c++;
55537                 }
55538             }
55539             return c;
55540         }
55541         return this.config.length;
55542     },
55543
55544     /**
55545      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55546      * @param {Function} fn
55547      * @param {Object} scope (optional)
55548      * @return {Array} result
55549      */
55550     getColumnsBy : function(fn, scope){
55551         var r = [];
55552         for(var i = 0, len = this.config.length; i < len; i++){
55553             var c = this.config[i];
55554             if(fn.call(scope||this, c, i) === true){
55555                 r[r.length] = c;
55556             }
55557         }
55558         return r;
55559     },
55560
55561     /**
55562      * Returns true if the specified column is sortable.
55563      * @param {Number} col The column index
55564      * @return {Boolean}
55565      */
55566     isSortable : function(col){
55567         if(typeof this.config[col].sortable == "undefined"){
55568             return this.defaultSortable;
55569         }
55570         return this.config[col].sortable;
55571     },
55572
55573     /**
55574      * Returns the rendering (formatting) function defined for the column.
55575      * @param {Number} col The column index.
55576      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55577      */
55578     getRenderer : function(col){
55579         if(!this.config[col].renderer){
55580             return Roo.grid.ColumnModel.defaultRenderer;
55581         }
55582         return this.config[col].renderer;
55583     },
55584
55585     /**
55586      * Sets the rendering (formatting) function for a column.
55587      * @param {Number} col The column index
55588      * @param {Function} fn The function to use to process the cell's raw data
55589      * to return HTML markup for the grid view. The render function is called with
55590      * the following parameters:<ul>
55591      * <li>Data value.</li>
55592      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55593      * <li>css A CSS style string to apply to the table cell.</li>
55594      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55595      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55596      * <li>Row index</li>
55597      * <li>Column index</li>
55598      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55599      */
55600     setRenderer : function(col, fn){
55601         this.config[col].renderer = fn;
55602     },
55603
55604     /**
55605      * Returns the width for the specified column.
55606      * @param {Number} col The column index
55607      * @return {Number}
55608      */
55609     getColumnWidth : function(col){
55610         return this.config[col].width * 1 || this.defaultWidth;
55611     },
55612
55613     /**
55614      * Sets the width for a column.
55615      * @param {Number} col The column index
55616      * @param {Number} width The new width
55617      */
55618     setColumnWidth : function(col, width, suppressEvent){
55619         this.config[col].width = width;
55620         this.totalWidth = null;
55621         if(!suppressEvent){
55622              this.fireEvent("widthchange", this, col, width);
55623         }
55624     },
55625
55626     /**
55627      * Returns the total width of all columns.
55628      * @param {Boolean} includeHidden True to include hidden column widths
55629      * @return {Number}
55630      */
55631     getTotalWidth : function(includeHidden){
55632         if(!this.totalWidth){
55633             this.totalWidth = 0;
55634             for(var i = 0, len = this.config.length; i < len; i++){
55635                 if(includeHidden || !this.isHidden(i)){
55636                     this.totalWidth += this.getColumnWidth(i);
55637                 }
55638             }
55639         }
55640         return this.totalWidth;
55641     },
55642
55643     /**
55644      * Returns the header for the specified column.
55645      * @param {Number} col The column index
55646      * @return {String}
55647      */
55648     getColumnHeader : function(col){
55649         return this.config[col].header;
55650     },
55651
55652     /**
55653      * Sets the header for a column.
55654      * @param {Number} col The column index
55655      * @param {String} header The new header
55656      */
55657     setColumnHeader : function(col, header){
55658         this.config[col].header = header;
55659         this.fireEvent("headerchange", this, col, header);
55660     },
55661
55662     /**
55663      * Returns the tooltip for the specified column.
55664      * @param {Number} col The column index
55665      * @return {String}
55666      */
55667     getColumnTooltip : function(col){
55668             return this.config[col].tooltip;
55669     },
55670     /**
55671      * Sets the tooltip for a column.
55672      * @param {Number} col The column index
55673      * @param {String} tooltip The new tooltip
55674      */
55675     setColumnTooltip : function(col, tooltip){
55676             this.config[col].tooltip = tooltip;
55677     },
55678
55679     /**
55680      * Returns the dataIndex for the specified column.
55681      * @param {Number} col The column index
55682      * @return {Number}
55683      */
55684     getDataIndex : function(col){
55685         return this.config[col].dataIndex;
55686     },
55687
55688     /**
55689      * Sets the dataIndex for a column.
55690      * @param {Number} col The column index
55691      * @param {Number} dataIndex The new dataIndex
55692      */
55693     setDataIndex : function(col, dataIndex){
55694         this.config[col].dataIndex = dataIndex;
55695     },
55696
55697     
55698     
55699     /**
55700      * Returns true if the cell is editable.
55701      * @param {Number} colIndex The column index
55702      * @param {Number} rowIndex The row index
55703      * @return {Boolean}
55704      */
55705     isCellEditable : function(colIndex, rowIndex){
55706         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55707     },
55708
55709     /**
55710      * Returns the editor defined for the cell/column.
55711      * return false or null to disable editing.
55712      * @param {Number} colIndex The column index
55713      * @param {Number} rowIndex The row index
55714      * @return {Object}
55715      */
55716     getCellEditor : function(colIndex, rowIndex){
55717         return this.config[colIndex].editor;
55718     },
55719
55720     /**
55721      * Sets if a column is editable.
55722      * @param {Number} col The column index
55723      * @param {Boolean} editable True if the column is editable
55724      */
55725     setEditable : function(col, editable){
55726         this.config[col].editable = editable;
55727     },
55728
55729
55730     /**
55731      * Returns true if the column is hidden.
55732      * @param {Number} colIndex The column index
55733      * @return {Boolean}
55734      */
55735     isHidden : function(colIndex){
55736         return this.config[colIndex].hidden;
55737     },
55738
55739
55740     /**
55741      * Returns true if the column width cannot be changed
55742      */
55743     isFixed : function(colIndex){
55744         return this.config[colIndex].fixed;
55745     },
55746
55747     /**
55748      * Returns true if the column can be resized
55749      * @return {Boolean}
55750      */
55751     isResizable : function(colIndex){
55752         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55753     },
55754     /**
55755      * Sets if a column is hidden.
55756      * @param {Number} colIndex The column index
55757      * @param {Boolean} hidden True if the column is hidden
55758      */
55759     setHidden : function(colIndex, hidden){
55760         this.config[colIndex].hidden = hidden;
55761         this.totalWidth = null;
55762         this.fireEvent("hiddenchange", this, colIndex, hidden);
55763     },
55764
55765     /**
55766      * Sets the editor for a column.
55767      * @param {Number} col The column index
55768      * @param {Object} editor The editor object
55769      */
55770     setEditor : function(col, editor){
55771         this.config[col].editor = editor;
55772     }
55773 });
55774
55775 Roo.grid.ColumnModel.defaultRenderer = function(value){
55776         if(typeof value == "string" && value.length < 1){
55777             return "&#160;";
55778         }
55779         return value;
55780 };
55781
55782 // Alias for backwards compatibility
55783 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55784 /*
55785  * Based on:
55786  * Ext JS Library 1.1.1
55787  * Copyright(c) 2006-2007, Ext JS, LLC.
55788  *
55789  * Originally Released Under LGPL - original licence link has changed is not relivant.
55790  *
55791  * Fork - LGPL
55792  * <script type="text/javascript">
55793  */
55794
55795 /**
55796  * @class Roo.grid.AbstractSelectionModel
55797  * @extends Roo.util.Observable
55798  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55799  * implemented by descendant classes.  This class should not be directly instantiated.
55800  * @constructor
55801  */
55802 Roo.grid.AbstractSelectionModel = function(){
55803     this.locked = false;
55804     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55805 };
55806
55807 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55808     /** @ignore Called by the grid automatically. Do not call directly. */
55809     init : function(grid){
55810         this.grid = grid;
55811         this.initEvents();
55812     },
55813
55814     /**
55815      * Locks the selections.
55816      */
55817     lock : function(){
55818         this.locked = true;
55819     },
55820
55821     /**
55822      * Unlocks the selections.
55823      */
55824     unlock : function(){
55825         this.locked = false;
55826     },
55827
55828     /**
55829      * Returns true if the selections are locked.
55830      * @return {Boolean}
55831      */
55832     isLocked : function(){
55833         return this.locked;
55834     }
55835 });/*
55836  * Based on:
55837  * Ext JS Library 1.1.1
55838  * Copyright(c) 2006-2007, Ext JS, LLC.
55839  *
55840  * Originally Released Under LGPL - original licence link has changed is not relivant.
55841  *
55842  * Fork - LGPL
55843  * <script type="text/javascript">
55844  */
55845 /**
55846  * @extends Roo.grid.AbstractSelectionModel
55847  * @class Roo.grid.RowSelectionModel
55848  * The default SelectionModel used by {@link Roo.grid.Grid}.
55849  * It supports multiple selections and keyboard selection/navigation. 
55850  * @constructor
55851  * @param {Object} config
55852  */
55853 Roo.grid.RowSelectionModel = function(config){
55854     Roo.apply(this, config);
55855     this.selections = new Roo.util.MixedCollection(false, function(o){
55856         return o.id;
55857     });
55858
55859     this.last = false;
55860     this.lastActive = false;
55861
55862     this.addEvents({
55863         /**
55864              * @event selectionchange
55865              * Fires when the selection changes
55866              * @param {SelectionModel} this
55867              */
55868             "selectionchange" : true,
55869         /**
55870              * @event afterselectionchange
55871              * Fires after the selection changes (eg. by key press or clicking)
55872              * @param {SelectionModel} this
55873              */
55874             "afterselectionchange" : true,
55875         /**
55876              * @event beforerowselect
55877              * Fires when a row is selected being selected, return false to cancel.
55878              * @param {SelectionModel} this
55879              * @param {Number} rowIndex The selected index
55880              * @param {Boolean} keepExisting False if other selections will be cleared
55881              */
55882             "beforerowselect" : true,
55883         /**
55884              * @event rowselect
55885              * Fires when a row is selected.
55886              * @param {SelectionModel} this
55887              * @param {Number} rowIndex The selected index
55888              * @param {Roo.data.Record} r The record
55889              */
55890             "rowselect" : true,
55891         /**
55892              * @event rowdeselect
55893              * Fires when a row is deselected.
55894              * @param {SelectionModel} this
55895              * @param {Number} rowIndex The selected index
55896              */
55897         "rowdeselect" : true
55898     });
55899     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55900     this.locked = false;
55901 };
55902
55903 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55904     /**
55905      * @cfg {Boolean} singleSelect
55906      * True to allow selection of only one row at a time (defaults to false)
55907      */
55908     singleSelect : false,
55909
55910     // private
55911     initEvents : function(){
55912
55913         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55914             this.grid.on("mousedown", this.handleMouseDown, this);
55915         }else{ // allow click to work like normal
55916             this.grid.on("rowclick", this.handleDragableRowClick, this);
55917         }
55918
55919         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55920             "up" : function(e){
55921                 if(!e.shiftKey){
55922                     this.selectPrevious(e.shiftKey);
55923                 }else if(this.last !== false && this.lastActive !== false){
55924                     var last = this.last;
55925                     this.selectRange(this.last,  this.lastActive-1);
55926                     this.grid.getView().focusRow(this.lastActive);
55927                     if(last !== false){
55928                         this.last = last;
55929                     }
55930                 }else{
55931                     this.selectFirstRow();
55932                 }
55933                 this.fireEvent("afterselectionchange", this);
55934             },
55935             "down" : function(e){
55936                 if(!e.shiftKey){
55937                     this.selectNext(e.shiftKey);
55938                 }else if(this.last !== false && this.lastActive !== false){
55939                     var last = this.last;
55940                     this.selectRange(this.last,  this.lastActive+1);
55941                     this.grid.getView().focusRow(this.lastActive);
55942                     if(last !== false){
55943                         this.last = last;
55944                     }
55945                 }else{
55946                     this.selectFirstRow();
55947                 }
55948                 this.fireEvent("afterselectionchange", this);
55949             },
55950             scope: this
55951         });
55952
55953         var view = this.grid.view;
55954         view.on("refresh", this.onRefresh, this);
55955         view.on("rowupdated", this.onRowUpdated, this);
55956         view.on("rowremoved", this.onRemove, this);
55957     },
55958
55959     // private
55960     onRefresh : function(){
55961         var ds = this.grid.dataSource, i, v = this.grid.view;
55962         var s = this.selections;
55963         s.each(function(r){
55964             if((i = ds.indexOfId(r.id)) != -1){
55965                 v.onRowSelect(i);
55966                 s.add(ds.getAt(i)); // updating the selection relate data
55967             }else{
55968                 s.remove(r);
55969             }
55970         });
55971     },
55972
55973     // private
55974     onRemove : function(v, index, r){
55975         this.selections.remove(r);
55976     },
55977
55978     // private
55979     onRowUpdated : function(v, index, r){
55980         if(this.isSelected(r)){
55981             v.onRowSelect(index);
55982         }
55983     },
55984
55985     /**
55986      * Select records.
55987      * @param {Array} records The records to select
55988      * @param {Boolean} keepExisting (optional) True to keep existing selections
55989      */
55990     selectRecords : function(records, keepExisting){
55991         if(!keepExisting){
55992             this.clearSelections();
55993         }
55994         var ds = this.grid.dataSource;
55995         for(var i = 0, len = records.length; i < len; i++){
55996             this.selectRow(ds.indexOf(records[i]), true);
55997         }
55998     },
55999
56000     /**
56001      * Gets the number of selected rows.
56002      * @return {Number}
56003      */
56004     getCount : function(){
56005         return this.selections.length;
56006     },
56007
56008     /**
56009      * Selects the first row in the grid.
56010      */
56011     selectFirstRow : function(){
56012         this.selectRow(0);
56013     },
56014
56015     /**
56016      * Select the last row.
56017      * @param {Boolean} keepExisting (optional) True to keep existing selections
56018      */
56019     selectLastRow : function(keepExisting){
56020         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
56021     },
56022
56023     /**
56024      * Selects the row immediately following the last selected row.
56025      * @param {Boolean} keepExisting (optional) True to keep existing selections
56026      */
56027     selectNext : function(keepExisting){
56028         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
56029             this.selectRow(this.last+1, keepExisting);
56030             this.grid.getView().focusRow(this.last);
56031         }
56032     },
56033
56034     /**
56035      * Selects the row that precedes the last selected row.
56036      * @param {Boolean} keepExisting (optional) True to keep existing selections
56037      */
56038     selectPrevious : function(keepExisting){
56039         if(this.last){
56040             this.selectRow(this.last-1, keepExisting);
56041             this.grid.getView().focusRow(this.last);
56042         }
56043     },
56044
56045     /**
56046      * Returns the selected records
56047      * @return {Array} Array of selected records
56048      */
56049     getSelections : function(){
56050         return [].concat(this.selections.items);
56051     },
56052
56053     /**
56054      * Returns the first selected record.
56055      * @return {Record}
56056      */
56057     getSelected : function(){
56058         return this.selections.itemAt(0);
56059     },
56060
56061
56062     /**
56063      * Clears all selections.
56064      */
56065     clearSelections : function(fast){
56066         if(this.locked) return;
56067         if(fast !== true){
56068             var ds = this.grid.dataSource;
56069             var s = this.selections;
56070             s.each(function(r){
56071                 this.deselectRow(ds.indexOfId(r.id));
56072             }, this);
56073             s.clear();
56074         }else{
56075             this.selections.clear();
56076         }
56077         this.last = false;
56078     },
56079
56080
56081     /**
56082      * Selects all rows.
56083      */
56084     selectAll : function(){
56085         if(this.locked) return;
56086         this.selections.clear();
56087         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56088             this.selectRow(i, true);
56089         }
56090     },
56091
56092     /**
56093      * Returns True if there is a selection.
56094      * @return {Boolean}
56095      */
56096     hasSelection : function(){
56097         return this.selections.length > 0;
56098     },
56099
56100     /**
56101      * Returns True if the specified row is selected.
56102      * @param {Number/Record} record The record or index of the record to check
56103      * @return {Boolean}
56104      */
56105     isSelected : function(index){
56106         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56107         return (r && this.selections.key(r.id) ? true : false);
56108     },
56109
56110     /**
56111      * Returns True if the specified record id is selected.
56112      * @param {String} id The id of record to check
56113      * @return {Boolean}
56114      */
56115     isIdSelected : function(id){
56116         return (this.selections.key(id) ? true : false);
56117     },
56118
56119     // private
56120     handleMouseDown : function(e, t){
56121         var view = this.grid.getView(), rowIndex;
56122         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56123             return;
56124         };
56125         if(e.shiftKey && this.last !== false){
56126             var last = this.last;
56127             this.selectRange(last, rowIndex, e.ctrlKey);
56128             this.last = last; // reset the last
56129             view.focusRow(rowIndex);
56130         }else{
56131             var isSelected = this.isSelected(rowIndex);
56132             if(e.button !== 0 && isSelected){
56133                 view.focusRow(rowIndex);
56134             }else if(e.ctrlKey && isSelected){
56135                 this.deselectRow(rowIndex);
56136             }else if(!isSelected){
56137                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56138                 view.focusRow(rowIndex);
56139             }
56140         }
56141         this.fireEvent("afterselectionchange", this);
56142     },
56143     // private
56144     handleDragableRowClick :  function(grid, rowIndex, e) 
56145     {
56146         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56147             this.selectRow(rowIndex, false);
56148             grid.view.focusRow(rowIndex);
56149              this.fireEvent("afterselectionchange", this);
56150         }
56151     },
56152     
56153     /**
56154      * Selects multiple rows.
56155      * @param {Array} rows Array of the indexes of the row to select
56156      * @param {Boolean} keepExisting (optional) True to keep existing selections
56157      */
56158     selectRows : function(rows, keepExisting){
56159         if(!keepExisting){
56160             this.clearSelections();
56161         }
56162         for(var i = 0, len = rows.length; i < len; i++){
56163             this.selectRow(rows[i], true);
56164         }
56165     },
56166
56167     /**
56168      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56169      * @param {Number} startRow The index of the first row in the range
56170      * @param {Number} endRow The index of the last row in the range
56171      * @param {Boolean} keepExisting (optional) True to retain existing selections
56172      */
56173     selectRange : function(startRow, endRow, keepExisting){
56174         if(this.locked) return;
56175         if(!keepExisting){
56176             this.clearSelections();
56177         }
56178         if(startRow <= endRow){
56179             for(var i = startRow; i <= endRow; i++){
56180                 this.selectRow(i, true);
56181             }
56182         }else{
56183             for(var i = startRow; i >= endRow; i--){
56184                 this.selectRow(i, true);
56185             }
56186         }
56187     },
56188
56189     /**
56190      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56191      * @param {Number} startRow The index of the first row in the range
56192      * @param {Number} endRow The index of the last row in the range
56193      */
56194     deselectRange : function(startRow, endRow, preventViewNotify){
56195         if(this.locked) return;
56196         for(var i = startRow; i <= endRow; i++){
56197             this.deselectRow(i, preventViewNotify);
56198         }
56199     },
56200
56201     /**
56202      * Selects a row.
56203      * @param {Number} row The index of the row to select
56204      * @param {Boolean} keepExisting (optional) True to keep existing selections
56205      */
56206     selectRow : function(index, keepExisting, preventViewNotify){
56207         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56208         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56209             if(!keepExisting || this.singleSelect){
56210                 this.clearSelections();
56211             }
56212             var r = this.grid.dataSource.getAt(index);
56213             this.selections.add(r);
56214             this.last = this.lastActive = index;
56215             if(!preventViewNotify){
56216                 this.grid.getView().onRowSelect(index);
56217             }
56218             this.fireEvent("rowselect", this, index, r);
56219             this.fireEvent("selectionchange", this);
56220         }
56221     },
56222
56223     /**
56224      * Deselects a row.
56225      * @param {Number} row The index of the row to deselect
56226      */
56227     deselectRow : function(index, preventViewNotify){
56228         if(this.locked) return;
56229         if(this.last == index){
56230             this.last = false;
56231         }
56232         if(this.lastActive == index){
56233             this.lastActive = false;
56234         }
56235         var r = this.grid.dataSource.getAt(index);
56236         this.selections.remove(r);
56237         if(!preventViewNotify){
56238             this.grid.getView().onRowDeselect(index);
56239         }
56240         this.fireEvent("rowdeselect", this, index);
56241         this.fireEvent("selectionchange", this);
56242     },
56243
56244     // private
56245     restoreLast : function(){
56246         if(this._last){
56247             this.last = this._last;
56248         }
56249     },
56250
56251     // private
56252     acceptsNav : function(row, col, cm){
56253         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56254     },
56255
56256     // private
56257     onEditorKey : function(field, e){
56258         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56259         if(k == e.TAB){
56260             e.stopEvent();
56261             ed.completeEdit();
56262             if(e.shiftKey){
56263                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56264             }else{
56265                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56266             }
56267         }else if(k == e.ENTER && !e.ctrlKey){
56268             e.stopEvent();
56269             ed.completeEdit();
56270             if(e.shiftKey){
56271                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56272             }else{
56273                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56274             }
56275         }else if(k == e.ESC){
56276             ed.cancelEdit();
56277         }
56278         if(newCell){
56279             g.startEditing(newCell[0], newCell[1]);
56280         }
56281     }
56282 });/*
56283  * Based on:
56284  * Ext JS Library 1.1.1
56285  * Copyright(c) 2006-2007, Ext JS, LLC.
56286  *
56287  * Originally Released Under LGPL - original licence link has changed is not relivant.
56288  *
56289  * Fork - LGPL
56290  * <script type="text/javascript">
56291  */
56292 /**
56293  * @class Roo.grid.CellSelectionModel
56294  * @extends Roo.grid.AbstractSelectionModel
56295  * This class provides the basic implementation for cell selection in a grid.
56296  * @constructor
56297  * @param {Object} config The object containing the configuration of this model.
56298  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56299  */
56300 Roo.grid.CellSelectionModel = function(config){
56301     Roo.apply(this, config);
56302
56303     this.selection = null;
56304
56305     this.addEvents({
56306         /**
56307              * @event beforerowselect
56308              * Fires before a cell is selected.
56309              * @param {SelectionModel} this
56310              * @param {Number} rowIndex The selected row index
56311              * @param {Number} colIndex The selected cell index
56312              */
56313             "beforecellselect" : true,
56314         /**
56315              * @event cellselect
56316              * Fires when a cell is selected.
56317              * @param {SelectionModel} this
56318              * @param {Number} rowIndex The selected row index
56319              * @param {Number} colIndex The selected cell index
56320              */
56321             "cellselect" : true,
56322         /**
56323              * @event selectionchange
56324              * Fires when the active selection changes.
56325              * @param {SelectionModel} this
56326              * @param {Object} selection null for no selection or an object (o) with two properties
56327                 <ul>
56328                 <li>o.record: the record object for the row the selection is in</li>
56329                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56330                 </ul>
56331              */
56332             "selectionchange" : true,
56333         /**
56334              * @event tabend
56335              * Fires when the tab (or enter) was pressed on the last editable cell
56336              * You can use this to trigger add new row.
56337              * @param {SelectionModel} this
56338              */
56339             "tabend" : true,
56340          /**
56341              * @event beforeeditnext
56342              * Fires before the next editable sell is made active
56343              * You can use this to skip to another cell or fire the tabend
56344              *    if you set cell to false
56345              * @param {Object} eventdata object : { cell : [ row, col ] } 
56346              */
56347             "beforeeditnext" : true
56348     });
56349     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56350 };
56351
56352 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56353     
56354     enter_is_tab: false,
56355
56356     /** @ignore */
56357     initEvents : function(){
56358         this.grid.on("mousedown", this.handleMouseDown, this);
56359         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56360         var view = this.grid.view;
56361         view.on("refresh", this.onViewChange, this);
56362         view.on("rowupdated", this.onRowUpdated, this);
56363         view.on("beforerowremoved", this.clearSelections, this);
56364         view.on("beforerowsinserted", this.clearSelections, this);
56365         if(this.grid.isEditor){
56366             this.grid.on("beforeedit", this.beforeEdit,  this);
56367         }
56368     },
56369
56370         //private
56371     beforeEdit : function(e){
56372         this.select(e.row, e.column, false, true, e.record);
56373     },
56374
56375         //private
56376     onRowUpdated : function(v, index, r){
56377         if(this.selection && this.selection.record == r){
56378             v.onCellSelect(index, this.selection.cell[1]);
56379         }
56380     },
56381
56382         //private
56383     onViewChange : function(){
56384         this.clearSelections(true);
56385     },
56386
56387         /**
56388          * Returns the currently selected cell,.
56389          * @return {Array} The selected cell (row, column) or null if none selected.
56390          */
56391     getSelectedCell : function(){
56392         return this.selection ? this.selection.cell : null;
56393     },
56394
56395     /**
56396      * Clears all selections.
56397      * @param {Boolean} true to prevent the gridview from being notified about the change.
56398      */
56399     clearSelections : function(preventNotify){
56400         var s = this.selection;
56401         if(s){
56402             if(preventNotify !== true){
56403                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56404             }
56405             this.selection = null;
56406             this.fireEvent("selectionchange", this, null);
56407         }
56408     },
56409
56410     /**
56411      * Returns true if there is a selection.
56412      * @return {Boolean}
56413      */
56414     hasSelection : function(){
56415         return this.selection ? true : false;
56416     },
56417
56418     /** @ignore */
56419     handleMouseDown : function(e, t){
56420         var v = this.grid.getView();
56421         if(this.isLocked()){
56422             return;
56423         };
56424         var row = v.findRowIndex(t);
56425         var cell = v.findCellIndex(t);
56426         if(row !== false && cell !== false){
56427             this.select(row, cell);
56428         }
56429     },
56430
56431     /**
56432      * Selects a cell.
56433      * @param {Number} rowIndex
56434      * @param {Number} collIndex
56435      */
56436     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56437         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56438             this.clearSelections();
56439             r = r || this.grid.dataSource.getAt(rowIndex);
56440             this.selection = {
56441                 record : r,
56442                 cell : [rowIndex, colIndex]
56443             };
56444             if(!preventViewNotify){
56445                 var v = this.grid.getView();
56446                 v.onCellSelect(rowIndex, colIndex);
56447                 if(preventFocus !== true){
56448                     v.focusCell(rowIndex, colIndex);
56449                 }
56450             }
56451             this.fireEvent("cellselect", this, rowIndex, colIndex);
56452             this.fireEvent("selectionchange", this, this.selection);
56453         }
56454     },
56455
56456         //private
56457     isSelectable : function(rowIndex, colIndex, cm){
56458         return !cm.isHidden(colIndex);
56459     },
56460
56461     /** @ignore */
56462     handleKeyDown : function(e){
56463         //Roo.log('Cell Sel Model handleKeyDown');
56464         if(!e.isNavKeyPress()){
56465             return;
56466         }
56467         var g = this.grid, s = this.selection;
56468         if(!s){
56469             e.stopEvent();
56470             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56471             if(cell){
56472                 this.select(cell[0], cell[1]);
56473             }
56474             return;
56475         }
56476         var sm = this;
56477         var walk = function(row, col, step){
56478             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56479         };
56480         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56481         var newCell;
56482
56483       
56484
56485         switch(k){
56486             case e.TAB:
56487                 // handled by onEditorKey
56488                 if (g.isEditor && g.editing) {
56489                     return;
56490                 }
56491                 if(e.shiftKey) {
56492                     newCell = walk(r, c-1, -1);
56493                 } else {
56494                     newCell = walk(r, c+1, 1);
56495                 }
56496                 break;
56497             
56498             case e.DOWN:
56499                newCell = walk(r+1, c, 1);
56500                 break;
56501             
56502             case e.UP:
56503                 newCell = walk(r-1, c, -1);
56504                 break;
56505             
56506             case e.RIGHT:
56507                 newCell = walk(r, c+1, 1);
56508                 break;
56509             
56510             case e.LEFT:
56511                 newCell = walk(r, c-1, -1);
56512                 break;
56513             
56514             case e.ENTER:
56515                 
56516                 if(g.isEditor && !g.editing){
56517                    g.startEditing(r, c);
56518                    e.stopEvent();
56519                    return;
56520                 }
56521                 
56522                 
56523              break;
56524         };
56525         if(newCell){
56526             this.select(newCell[0], newCell[1]);
56527             e.stopEvent();
56528             
56529         }
56530     },
56531
56532     acceptsNav : function(row, col, cm){
56533         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56534     },
56535     /**
56536      * Selects a cell.
56537      * @param {Number} field (not used) - as it's normally used as a listener
56538      * @param {Number} e - event - fake it by using
56539      *
56540      * var e = Roo.EventObjectImpl.prototype;
56541      * e.keyCode = e.TAB
56542      *
56543      * 
56544      */
56545     onEditorKey : function(field, e){
56546         
56547         var k = e.getKey(),
56548             newCell,
56549             g = this.grid,
56550             ed = g.activeEditor,
56551             forward = false;
56552         ///Roo.log('onEditorKey' + k);
56553         
56554         
56555         if (this.enter_is_tab && k == e.ENTER) {
56556             k = e.TAB;
56557         }
56558         
56559         if(k == e.TAB){
56560             if(e.shiftKey){
56561                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56562             }else{
56563                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56564                 forward = true;
56565             }
56566             
56567             e.stopEvent();
56568             
56569         } else if(k == e.ENTER &&  !e.ctrlKey){
56570             ed.completeEdit();
56571             e.stopEvent();
56572             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56573         
56574                 } else if(k == e.ESC){
56575             ed.cancelEdit();
56576         }
56577                 
56578         if (newCell) {
56579             var ecall = { cell : newCell, forward : forward };
56580             this.fireEvent('beforeeditnext', ecall );
56581             newCell = ecall.cell;
56582                         forward = ecall.forward;
56583         }
56584                 
56585         if(newCell){
56586             //Roo.log('next cell after edit');
56587             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56588         } else if (forward) {
56589             // tabbed past last
56590             this.fireEvent.defer(100, this, ['tabend',this]);
56591         }
56592     }
56593 });/*
56594  * Based on:
56595  * Ext JS Library 1.1.1
56596  * Copyright(c) 2006-2007, Ext JS, LLC.
56597  *
56598  * Originally Released Under LGPL - original licence link has changed is not relivant.
56599  *
56600  * Fork - LGPL
56601  * <script type="text/javascript">
56602  */
56603  
56604 /**
56605  * @class Roo.grid.EditorGrid
56606  * @extends Roo.grid.Grid
56607  * Class for creating and editable grid.
56608  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56609  * The container MUST have some type of size defined for the grid to fill. The container will be 
56610  * automatically set to position relative if it isn't already.
56611  * @param {Object} dataSource The data model to bind to
56612  * @param {Object} colModel The column model with info about this grid's columns
56613  */
56614 Roo.grid.EditorGrid = function(container, config){
56615     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56616     this.getGridEl().addClass("xedit-grid");
56617
56618     if(!this.selModel){
56619         this.selModel = new Roo.grid.CellSelectionModel();
56620     }
56621
56622     this.activeEditor = null;
56623
56624         this.addEvents({
56625             /**
56626              * @event beforeedit
56627              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56628              * <ul style="padding:5px;padding-left:16px;">
56629              * <li>grid - This grid</li>
56630              * <li>record - The record being edited</li>
56631              * <li>field - The field name being edited</li>
56632              * <li>value - The value for the field being edited.</li>
56633              * <li>row - The grid row index</li>
56634              * <li>column - The grid column index</li>
56635              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56636              * </ul>
56637              * @param {Object} e An edit event (see above for description)
56638              */
56639             "beforeedit" : true,
56640             /**
56641              * @event afteredit
56642              * Fires after a cell is edited. <br />
56643              * <ul style="padding:5px;padding-left:16px;">
56644              * <li>grid - This grid</li>
56645              * <li>record - The record being edited</li>
56646              * <li>field - The field name being edited</li>
56647              * <li>value - The value being set</li>
56648              * <li>originalValue - The original value for the field, before the edit.</li>
56649              * <li>row - The grid row index</li>
56650              * <li>column - The grid column index</li>
56651              * </ul>
56652              * @param {Object} e An edit event (see above for description)
56653              */
56654             "afteredit" : true,
56655             /**
56656              * @event validateedit
56657              * Fires after a cell is edited, but before the value is set in the record. 
56658          * You can use this to modify the value being set in the field, Return false
56659              * to cancel the change. The edit event object has the following properties <br />
56660              * <ul style="padding:5px;padding-left:16px;">
56661          * <li>editor - This editor</li>
56662              * <li>grid - This grid</li>
56663              * <li>record - The record being edited</li>
56664              * <li>field - The field name being edited</li>
56665              * <li>value - The value being set</li>
56666              * <li>originalValue - The original value for the field, before the edit.</li>
56667              * <li>row - The grid row index</li>
56668              * <li>column - The grid column index</li>
56669              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56670              * </ul>
56671              * @param {Object} e An edit event (see above for description)
56672              */
56673             "validateedit" : true
56674         });
56675     this.on("bodyscroll", this.stopEditing,  this);
56676     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56677 };
56678
56679 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56680     /**
56681      * @cfg {Number} clicksToEdit
56682      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56683      */
56684     clicksToEdit: 2,
56685
56686     // private
56687     isEditor : true,
56688     // private
56689     trackMouseOver: false, // causes very odd FF errors
56690
56691     onCellDblClick : function(g, row, col){
56692         this.startEditing(row, col);
56693     },
56694
56695     onEditComplete : function(ed, value, startValue){
56696         this.editing = false;
56697         this.activeEditor = null;
56698         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56699         var r = ed.record;
56700         var field = this.colModel.getDataIndex(ed.col);
56701         var e = {
56702             grid: this,
56703             record: r,
56704             field: field,
56705             originalValue: startValue,
56706             value: value,
56707             row: ed.row,
56708             column: ed.col,
56709             cancel:false,
56710             editor: ed
56711         };
56712         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56713         cell.show();
56714           
56715         if(String(value) !== String(startValue)){
56716             
56717             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56718                 r.set(field, e.value);
56719                 // if we are dealing with a combo box..
56720                 // then we also set the 'name' colum to be the displayField
56721                 if (ed.field.displayField && ed.field.name) {
56722                     r.set(ed.field.name, ed.field.el.dom.value);
56723                 }
56724                 
56725                 delete e.cancel; //?? why!!!
56726                 this.fireEvent("afteredit", e);
56727             }
56728         } else {
56729             this.fireEvent("afteredit", e); // always fire it!
56730         }
56731         this.view.focusCell(ed.row, ed.col);
56732     },
56733
56734     /**
56735      * Starts editing the specified for the specified row/column
56736      * @param {Number} rowIndex
56737      * @param {Number} colIndex
56738      */
56739     startEditing : function(row, col){
56740         this.stopEditing();
56741         if(this.colModel.isCellEditable(col, row)){
56742             this.view.ensureVisible(row, col, true);
56743           
56744             var r = this.dataSource.getAt(row);
56745             var field = this.colModel.getDataIndex(col);
56746             var cell = Roo.get(this.view.getCell(row,col));
56747             var e = {
56748                 grid: this,
56749                 record: r,
56750                 field: field,
56751                 value: r.data[field],
56752                 row: row,
56753                 column: col,
56754                 cancel:false 
56755             };
56756             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56757                 this.editing = true;
56758                 var ed = this.colModel.getCellEditor(col, row);
56759                 
56760                 if (!ed) {
56761                     return;
56762                 }
56763                 if(!ed.rendered){
56764                     ed.render(ed.parentEl || document.body);
56765                 }
56766                 ed.field.reset();
56767                
56768                 cell.hide();
56769                 
56770                 (function(){ // complex but required for focus issues in safari, ie and opera
56771                     ed.row = row;
56772                     ed.col = col;
56773                     ed.record = r;
56774                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56775                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56776                     this.activeEditor = ed;
56777                     var v = r.data[field];
56778                     ed.startEdit(this.view.getCell(row, col), v);
56779                     // combo's with 'displayField and name set
56780                     if (ed.field.displayField && ed.field.name) {
56781                         ed.field.el.dom.value = r.data[ed.field.name];
56782                     }
56783                     
56784                     
56785                 }).defer(50, this);
56786             }
56787         }
56788     },
56789         
56790     /**
56791      * Stops any active editing
56792      */
56793     stopEditing : function(){
56794         if(this.activeEditor){
56795             this.activeEditor.completeEdit();
56796         }
56797         this.activeEditor = null;
56798     },
56799         
56800          /**
56801      * Called to get grid's drag proxy text, by default returns this.ddText.
56802      * @return {String}
56803      */
56804     getDragDropText : function(){
56805         var count = this.selModel.getSelectedCell() ? 1 : 0;
56806         return String.format(this.ddText, count, count == 1 ? '' : 's');
56807     }
56808         
56809 });/*
56810  * Based on:
56811  * Ext JS Library 1.1.1
56812  * Copyright(c) 2006-2007, Ext JS, LLC.
56813  *
56814  * Originally Released Under LGPL - original licence link has changed is not relivant.
56815  *
56816  * Fork - LGPL
56817  * <script type="text/javascript">
56818  */
56819
56820 // private - not really -- you end up using it !
56821 // This is a support class used internally by the Grid components
56822
56823 /**
56824  * @class Roo.grid.GridEditor
56825  * @extends Roo.Editor
56826  * Class for creating and editable grid elements.
56827  * @param {Object} config any settings (must include field)
56828  */
56829 Roo.grid.GridEditor = function(field, config){
56830     if (!config && field.field) {
56831         config = field;
56832         field = Roo.factory(config.field, Roo.form);
56833     }
56834     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56835     field.monitorTab = false;
56836 };
56837
56838 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56839     
56840     /**
56841      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56842      */
56843     
56844     alignment: "tl-tl",
56845     autoSize: "width",
56846     hideEl : false,
56847     cls: "x-small-editor x-grid-editor",
56848     shim:false,
56849     shadow:"frame"
56850 });/*
56851  * Based on:
56852  * Ext JS Library 1.1.1
56853  * Copyright(c) 2006-2007, Ext JS, LLC.
56854  *
56855  * Originally Released Under LGPL - original licence link has changed is not relivant.
56856  *
56857  * Fork - LGPL
56858  * <script type="text/javascript">
56859  */
56860   
56861
56862   
56863 Roo.grid.PropertyRecord = Roo.data.Record.create([
56864     {name:'name',type:'string'},  'value'
56865 ]);
56866
56867
56868 Roo.grid.PropertyStore = function(grid, source){
56869     this.grid = grid;
56870     this.store = new Roo.data.Store({
56871         recordType : Roo.grid.PropertyRecord
56872     });
56873     this.store.on('update', this.onUpdate,  this);
56874     if(source){
56875         this.setSource(source);
56876     }
56877     Roo.grid.PropertyStore.superclass.constructor.call(this);
56878 };
56879
56880
56881
56882 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56883     setSource : function(o){
56884         this.source = o;
56885         this.store.removeAll();
56886         var data = [];
56887         for(var k in o){
56888             if(this.isEditableValue(o[k])){
56889                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56890             }
56891         }
56892         this.store.loadRecords({records: data}, {}, true);
56893     },
56894
56895     onUpdate : function(ds, record, type){
56896         if(type == Roo.data.Record.EDIT){
56897             var v = record.data['value'];
56898             var oldValue = record.modified['value'];
56899             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56900                 this.source[record.id] = v;
56901                 record.commit();
56902                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56903             }else{
56904                 record.reject();
56905             }
56906         }
56907     },
56908
56909     getProperty : function(row){
56910        return this.store.getAt(row);
56911     },
56912
56913     isEditableValue: function(val){
56914         if(val && val instanceof Date){
56915             return true;
56916         }else if(typeof val == 'object' || typeof val == 'function'){
56917             return false;
56918         }
56919         return true;
56920     },
56921
56922     setValue : function(prop, value){
56923         this.source[prop] = value;
56924         this.store.getById(prop).set('value', value);
56925     },
56926
56927     getSource : function(){
56928         return this.source;
56929     }
56930 });
56931
56932 Roo.grid.PropertyColumnModel = function(grid, store){
56933     this.grid = grid;
56934     var g = Roo.grid;
56935     g.PropertyColumnModel.superclass.constructor.call(this, [
56936         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56937         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56938     ]);
56939     this.store = store;
56940     this.bselect = Roo.DomHelper.append(document.body, {
56941         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56942             {tag: 'option', value: 'true', html: 'true'},
56943             {tag: 'option', value: 'false', html: 'false'}
56944         ]
56945     });
56946     Roo.id(this.bselect);
56947     var f = Roo.form;
56948     this.editors = {
56949         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56950         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56951         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56952         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56953         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56954     };
56955     this.renderCellDelegate = this.renderCell.createDelegate(this);
56956     this.renderPropDelegate = this.renderProp.createDelegate(this);
56957 };
56958
56959 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56960     
56961     
56962     nameText : 'Name',
56963     valueText : 'Value',
56964     
56965     dateFormat : 'm/j/Y',
56966     
56967     
56968     renderDate : function(dateVal){
56969         return dateVal.dateFormat(this.dateFormat);
56970     },
56971
56972     renderBool : function(bVal){
56973         return bVal ? 'true' : 'false';
56974     },
56975
56976     isCellEditable : function(colIndex, rowIndex){
56977         return colIndex == 1;
56978     },
56979
56980     getRenderer : function(col){
56981         return col == 1 ?
56982             this.renderCellDelegate : this.renderPropDelegate;
56983     },
56984
56985     renderProp : function(v){
56986         return this.getPropertyName(v);
56987     },
56988
56989     renderCell : function(val){
56990         var rv = val;
56991         if(val instanceof Date){
56992             rv = this.renderDate(val);
56993         }else if(typeof val == 'boolean'){
56994             rv = this.renderBool(val);
56995         }
56996         return Roo.util.Format.htmlEncode(rv);
56997     },
56998
56999     getPropertyName : function(name){
57000         var pn = this.grid.propertyNames;
57001         return pn && pn[name] ? pn[name] : name;
57002     },
57003
57004     getCellEditor : function(colIndex, rowIndex){
57005         var p = this.store.getProperty(rowIndex);
57006         var n = p.data['name'], val = p.data['value'];
57007         
57008         if(typeof(this.grid.customEditors[n]) == 'string'){
57009             return this.editors[this.grid.customEditors[n]];
57010         }
57011         if(typeof(this.grid.customEditors[n]) != 'undefined'){
57012             return this.grid.customEditors[n];
57013         }
57014         if(val instanceof Date){
57015             return this.editors['date'];
57016         }else if(typeof val == 'number'){
57017             return this.editors['number'];
57018         }else if(typeof val == 'boolean'){
57019             return this.editors['boolean'];
57020         }else{
57021             return this.editors['string'];
57022         }
57023     }
57024 });
57025
57026 /**
57027  * @class Roo.grid.PropertyGrid
57028  * @extends Roo.grid.EditorGrid
57029  * This class represents the  interface of a component based property grid control.
57030  * <br><br>Usage:<pre><code>
57031  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57032       
57033  });
57034  // set any options
57035  grid.render();
57036  * </code></pre>
57037   
57038  * @constructor
57039  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57040  * The container MUST have some type of size defined for the grid to fill. The container will be
57041  * automatically set to position relative if it isn't already.
57042  * @param {Object} config A config object that sets properties on this grid.
57043  */
57044 Roo.grid.PropertyGrid = function(container, config){
57045     config = config || {};
57046     var store = new Roo.grid.PropertyStore(this);
57047     this.store = store;
57048     var cm = new Roo.grid.PropertyColumnModel(this, store);
57049     store.store.sort('name', 'ASC');
57050     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57051         ds: store.store,
57052         cm: cm,
57053         enableColLock:false,
57054         enableColumnMove:false,
57055         stripeRows:false,
57056         trackMouseOver: false,
57057         clicksToEdit:1
57058     }, config));
57059     this.getGridEl().addClass('x-props-grid');
57060     this.lastEditRow = null;
57061     this.on('columnresize', this.onColumnResize, this);
57062     this.addEvents({
57063          /**
57064              * @event beforepropertychange
57065              * Fires before a property changes (return false to stop?)
57066              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57067              * @param {String} id Record Id
57068              * @param {String} newval New Value
57069          * @param {String} oldval Old Value
57070              */
57071         "beforepropertychange": true,
57072         /**
57073              * @event propertychange
57074              * Fires after a property changes
57075              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57076              * @param {String} id Record Id
57077              * @param {String} newval New Value
57078          * @param {String} oldval Old Value
57079              */
57080         "propertychange": true
57081     });
57082     this.customEditors = this.customEditors || {};
57083 };
57084 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57085     
57086      /**
57087      * @cfg {Object} customEditors map of colnames=> custom editors.
57088      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57089      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57090      * false disables editing of the field.
57091          */
57092     
57093       /**
57094      * @cfg {Object} propertyNames map of property Names to their displayed value
57095          */
57096     
57097     render : function(){
57098         Roo.grid.PropertyGrid.superclass.render.call(this);
57099         this.autoSize.defer(100, this);
57100     },
57101
57102     autoSize : function(){
57103         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57104         if(this.view){
57105             this.view.fitColumns();
57106         }
57107     },
57108
57109     onColumnResize : function(){
57110         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57111         this.autoSize();
57112     },
57113     /**
57114      * Sets the data for the Grid
57115      * accepts a Key => Value object of all the elements avaiable.
57116      * @param {Object} data  to appear in grid.
57117      */
57118     setSource : function(source){
57119         this.store.setSource(source);
57120         //this.autoSize();
57121     },
57122     /**
57123      * Gets all the data from the grid.
57124      * @return {Object} data  data stored in grid
57125      */
57126     getSource : function(){
57127         return this.store.getSource();
57128     }
57129 });/*
57130   
57131  * Licence LGPL
57132  
57133  */
57134  
57135 /**
57136  * @class Roo.grid.Calendar
57137  * @extends Roo.util.Grid
57138  * This class extends the Grid to provide a calendar widget
57139  * <br><br>Usage:<pre><code>
57140  var grid = new Roo.grid.Calendar("my-container-id", {
57141      ds: myDataStore,
57142      cm: myColModel,
57143      selModel: mySelectionModel,
57144      autoSizeColumns: true,
57145      monitorWindowResize: false,
57146      trackMouseOver: true
57147      eventstore : real data store..
57148  });
57149  // set any options
57150  grid.render();
57151   
57152   * @constructor
57153  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57154  * The container MUST have some type of size defined for the grid to fill. The container will be
57155  * automatically set to position relative if it isn't already.
57156  * @param {Object} config A config object that sets properties on this grid.
57157  */
57158 Roo.grid.Calendar = function(container, config){
57159         // initialize the container
57160         this.container = Roo.get(container);
57161         this.container.update("");
57162         this.container.setStyle("overflow", "hidden");
57163     this.container.addClass('x-grid-container');
57164
57165     this.id = this.container.id;
57166
57167     Roo.apply(this, config);
57168     // check and correct shorthanded configs
57169     
57170     var rows = [];
57171     var d =1;
57172     for (var r = 0;r < 6;r++) {
57173         
57174         rows[r]=[];
57175         for (var c =0;c < 7;c++) {
57176             rows[r][c]= '';
57177         }
57178     }
57179     if (this.eventStore) {
57180         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57181         this.eventStore.on('load',this.onLoad, this);
57182         this.eventStore.on('beforeload',this.clearEvents, this);
57183          
57184     }
57185     
57186     this.dataSource = new Roo.data.Store({
57187             proxy: new Roo.data.MemoryProxy(rows),
57188             reader: new Roo.data.ArrayReader({}, [
57189                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57190     });
57191
57192     this.dataSource.load();
57193     this.ds = this.dataSource;
57194     this.ds.xmodule = this.xmodule || false;
57195     
57196     
57197     var cellRender = function(v,x,r)
57198     {
57199         return String.format(
57200             '<div class="fc-day  fc-widget-content"><div>' +
57201                 '<div class="fc-event-container"></div>' +
57202                 '<div class="fc-day-number">{0}</div>'+
57203                 
57204                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57205             '</div></div>', v);
57206     
57207     }
57208     
57209     
57210     this.colModel = new Roo.grid.ColumnModel( [
57211         {
57212             xtype: 'ColumnModel',
57213             xns: Roo.grid,
57214             dataIndex : 'weekday0',
57215             header : 'Sunday',
57216             renderer : cellRender
57217         },
57218         {
57219             xtype: 'ColumnModel',
57220             xns: Roo.grid,
57221             dataIndex : 'weekday1',
57222             header : 'Monday',
57223             renderer : cellRender
57224         },
57225         {
57226             xtype: 'ColumnModel',
57227             xns: Roo.grid,
57228             dataIndex : 'weekday2',
57229             header : 'Tuesday',
57230             renderer : cellRender
57231         },
57232         {
57233             xtype: 'ColumnModel',
57234             xns: Roo.grid,
57235             dataIndex : 'weekday3',
57236             header : 'Wednesday',
57237             renderer : cellRender
57238         },
57239         {
57240             xtype: 'ColumnModel',
57241             xns: Roo.grid,
57242             dataIndex : 'weekday4',
57243             header : 'Thursday',
57244             renderer : cellRender
57245         },
57246         {
57247             xtype: 'ColumnModel',
57248             xns: Roo.grid,
57249             dataIndex : 'weekday5',
57250             header : 'Friday',
57251             renderer : cellRender
57252         },
57253         {
57254             xtype: 'ColumnModel',
57255             xns: Roo.grid,
57256             dataIndex : 'weekday6',
57257             header : 'Saturday',
57258             renderer : cellRender
57259         }
57260     ]);
57261     this.cm = this.colModel;
57262     this.cm.xmodule = this.xmodule || false;
57263  
57264         
57265           
57266     //this.selModel = new Roo.grid.CellSelectionModel();
57267     //this.sm = this.selModel;
57268     //this.selModel.init(this);
57269     
57270     
57271     if(this.width){
57272         this.container.setWidth(this.width);
57273     }
57274
57275     if(this.height){
57276         this.container.setHeight(this.height);
57277     }
57278     /** @private */
57279         this.addEvents({
57280         // raw events
57281         /**
57282          * @event click
57283          * The raw click event for the entire grid.
57284          * @param {Roo.EventObject} e
57285          */
57286         "click" : true,
57287         /**
57288          * @event dblclick
57289          * The raw dblclick event for the entire grid.
57290          * @param {Roo.EventObject} e
57291          */
57292         "dblclick" : true,
57293         /**
57294          * @event contextmenu
57295          * The raw contextmenu event for the entire grid.
57296          * @param {Roo.EventObject} e
57297          */
57298         "contextmenu" : true,
57299         /**
57300          * @event mousedown
57301          * The raw mousedown event for the entire grid.
57302          * @param {Roo.EventObject} e
57303          */
57304         "mousedown" : true,
57305         /**
57306          * @event mouseup
57307          * The raw mouseup event for the entire grid.
57308          * @param {Roo.EventObject} e
57309          */
57310         "mouseup" : true,
57311         /**
57312          * @event mouseover
57313          * The raw mouseover event for the entire grid.
57314          * @param {Roo.EventObject} e
57315          */
57316         "mouseover" : true,
57317         /**
57318          * @event mouseout
57319          * The raw mouseout event for the entire grid.
57320          * @param {Roo.EventObject} e
57321          */
57322         "mouseout" : true,
57323         /**
57324          * @event keypress
57325          * The raw keypress event for the entire grid.
57326          * @param {Roo.EventObject} e
57327          */
57328         "keypress" : true,
57329         /**
57330          * @event keydown
57331          * The raw keydown event for the entire grid.
57332          * @param {Roo.EventObject} e
57333          */
57334         "keydown" : true,
57335
57336         // custom events
57337
57338         /**
57339          * @event cellclick
57340          * Fires when a cell is clicked
57341          * @param {Grid} this
57342          * @param {Number} rowIndex
57343          * @param {Number} columnIndex
57344          * @param {Roo.EventObject} e
57345          */
57346         "cellclick" : true,
57347         /**
57348          * @event celldblclick
57349          * Fires when a cell is double clicked
57350          * @param {Grid} this
57351          * @param {Number} rowIndex
57352          * @param {Number} columnIndex
57353          * @param {Roo.EventObject} e
57354          */
57355         "celldblclick" : true,
57356         /**
57357          * @event rowclick
57358          * Fires when a row is clicked
57359          * @param {Grid} this
57360          * @param {Number} rowIndex
57361          * @param {Roo.EventObject} e
57362          */
57363         "rowclick" : true,
57364         /**
57365          * @event rowdblclick
57366          * Fires when a row is double clicked
57367          * @param {Grid} this
57368          * @param {Number} rowIndex
57369          * @param {Roo.EventObject} e
57370          */
57371         "rowdblclick" : true,
57372         /**
57373          * @event headerclick
57374          * Fires when a header is clicked
57375          * @param {Grid} this
57376          * @param {Number} columnIndex
57377          * @param {Roo.EventObject} e
57378          */
57379         "headerclick" : true,
57380         /**
57381          * @event headerdblclick
57382          * Fires when a header cell is double clicked
57383          * @param {Grid} this
57384          * @param {Number} columnIndex
57385          * @param {Roo.EventObject} e
57386          */
57387         "headerdblclick" : true,
57388         /**
57389          * @event rowcontextmenu
57390          * Fires when a row is right clicked
57391          * @param {Grid} this
57392          * @param {Number} rowIndex
57393          * @param {Roo.EventObject} e
57394          */
57395         "rowcontextmenu" : true,
57396         /**
57397          * @event cellcontextmenu
57398          * Fires when a cell is right clicked
57399          * @param {Grid} this
57400          * @param {Number} rowIndex
57401          * @param {Number} cellIndex
57402          * @param {Roo.EventObject} e
57403          */
57404          "cellcontextmenu" : true,
57405         /**
57406          * @event headercontextmenu
57407          * Fires when a header is right clicked
57408          * @param {Grid} this
57409          * @param {Number} columnIndex
57410          * @param {Roo.EventObject} e
57411          */
57412         "headercontextmenu" : true,
57413         /**
57414          * @event bodyscroll
57415          * Fires when the body element is scrolled
57416          * @param {Number} scrollLeft
57417          * @param {Number} scrollTop
57418          */
57419         "bodyscroll" : true,
57420         /**
57421          * @event columnresize
57422          * Fires when the user resizes a column
57423          * @param {Number} columnIndex
57424          * @param {Number} newSize
57425          */
57426         "columnresize" : true,
57427         /**
57428          * @event columnmove
57429          * Fires when the user moves a column
57430          * @param {Number} oldIndex
57431          * @param {Number} newIndex
57432          */
57433         "columnmove" : true,
57434         /**
57435          * @event startdrag
57436          * Fires when row(s) start being dragged
57437          * @param {Grid} this
57438          * @param {Roo.GridDD} dd The drag drop object
57439          * @param {event} e The raw browser event
57440          */
57441         "startdrag" : true,
57442         /**
57443          * @event enddrag
57444          * Fires when a drag operation is complete
57445          * @param {Grid} this
57446          * @param {Roo.GridDD} dd The drag drop object
57447          * @param {event} e The raw browser event
57448          */
57449         "enddrag" : true,
57450         /**
57451          * @event dragdrop
57452          * Fires when dragged row(s) are dropped on a valid DD target
57453          * @param {Grid} this
57454          * @param {Roo.GridDD} dd The drag drop object
57455          * @param {String} targetId The target drag drop object
57456          * @param {event} e The raw browser event
57457          */
57458         "dragdrop" : true,
57459         /**
57460          * @event dragover
57461          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57462          * @param {Grid} this
57463          * @param {Roo.GridDD} dd The drag drop object
57464          * @param {String} targetId The target drag drop object
57465          * @param {event} e The raw browser event
57466          */
57467         "dragover" : true,
57468         /**
57469          * @event dragenter
57470          *  Fires when the dragged row(s) first cross another DD target while being dragged
57471          * @param {Grid} this
57472          * @param {Roo.GridDD} dd The drag drop object
57473          * @param {String} targetId The target drag drop object
57474          * @param {event} e The raw browser event
57475          */
57476         "dragenter" : true,
57477         /**
57478          * @event dragout
57479          * Fires when the dragged row(s) leave another DD target while being dragged
57480          * @param {Grid} this
57481          * @param {Roo.GridDD} dd The drag drop object
57482          * @param {String} targetId The target drag drop object
57483          * @param {event} e The raw browser event
57484          */
57485         "dragout" : true,
57486         /**
57487          * @event rowclass
57488          * Fires when a row is rendered, so you can change add a style to it.
57489          * @param {GridView} gridview   The grid view
57490          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57491          */
57492         'rowclass' : true,
57493
57494         /**
57495          * @event render
57496          * Fires when the grid is rendered
57497          * @param {Grid} grid
57498          */
57499         'render' : true,
57500             /**
57501              * @event select
57502              * Fires when a date is selected
57503              * @param {DatePicker} this
57504              * @param {Date} date The selected date
57505              */
57506         'select': true,
57507         /**
57508              * @event monthchange
57509              * Fires when the displayed month changes 
57510              * @param {DatePicker} this
57511              * @param {Date} date The selected month
57512              */
57513         'monthchange': true,
57514         /**
57515              * @event evententer
57516              * Fires when mouse over an event
57517              * @param {Calendar} this
57518              * @param {event} Event
57519              */
57520         'evententer': true,
57521         /**
57522              * @event eventleave
57523              * Fires when the mouse leaves an
57524              * @param {Calendar} this
57525              * @param {event}
57526              */
57527         'eventleave': true,
57528         /**
57529              * @event eventclick
57530              * Fires when the mouse click an
57531              * @param {Calendar} this
57532              * @param {event}
57533              */
57534         'eventclick': true,
57535         /**
57536              * @event eventrender
57537              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57538              * @param {Calendar} this
57539              * @param {data} data to be modified
57540              */
57541         'eventrender': true
57542         
57543     });
57544
57545     Roo.grid.Grid.superclass.constructor.call(this);
57546     this.on('render', function() {
57547         this.view.el.addClass('x-grid-cal'); 
57548         
57549         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57550
57551     },this);
57552     
57553     if (!Roo.grid.Calendar.style) {
57554         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57555             
57556             
57557             '.x-grid-cal .x-grid-col' :  {
57558                 height: 'auto !important',
57559                 'vertical-align': 'top'
57560             },
57561             '.x-grid-cal  .fc-event-hori' : {
57562                 height: '14px'
57563             }
57564              
57565             
57566         }, Roo.id());
57567     }
57568
57569     
57570     
57571 };
57572 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57573     /**
57574      * @cfg {Store} eventStore The store that loads events.
57575      */
57576     eventStore : 25,
57577
57578      
57579     activeDate : false,
57580     startDay : 0,
57581     autoWidth : true,
57582     monitorWindowResize : false,
57583
57584     
57585     resizeColumns : function() {
57586         var col = (this.view.el.getWidth() / 7) - 3;
57587         // loop through cols, and setWidth
57588         for(var i =0 ; i < 7 ; i++){
57589             this.cm.setColumnWidth(i, col);
57590         }
57591     },
57592      setDate :function(date) {
57593         
57594         Roo.log('setDate?');
57595         
57596         this.resizeColumns();
57597         var vd = this.activeDate;
57598         this.activeDate = date;
57599 //        if(vd && this.el){
57600 //            var t = date.getTime();
57601 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57602 //                Roo.log('using add remove');
57603 //                
57604 //                this.fireEvent('monthchange', this, date);
57605 //                
57606 //                this.cells.removeClass("fc-state-highlight");
57607 //                this.cells.each(function(c){
57608 //                   if(c.dateValue == t){
57609 //                       c.addClass("fc-state-highlight");
57610 //                       setTimeout(function(){
57611 //                            try{c.dom.firstChild.focus();}catch(e){}
57612 //                       }, 50);
57613 //                       return false;
57614 //                   }
57615 //                   return true;
57616 //                });
57617 //                return;
57618 //            }
57619 //        }
57620         
57621         var days = date.getDaysInMonth();
57622         
57623         var firstOfMonth = date.getFirstDateOfMonth();
57624         var startingPos = firstOfMonth.getDay()-this.startDay;
57625         
57626         if(startingPos < this.startDay){
57627             startingPos += 7;
57628         }
57629         
57630         var pm = date.add(Date.MONTH, -1);
57631         var prevStart = pm.getDaysInMonth()-startingPos;
57632 //        
57633         
57634         
57635         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57636         
57637         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57638         //this.cells.addClassOnOver('fc-state-hover');
57639         
57640         var cells = this.cells.elements;
57641         var textEls = this.textNodes;
57642         
57643         //Roo.each(cells, function(cell){
57644         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57645         //});
57646         
57647         days += startingPos;
57648
57649         // convert everything to numbers so it's fast
57650         var day = 86400000;
57651         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57652         //Roo.log(d);
57653         //Roo.log(pm);
57654         //Roo.log(prevStart);
57655         
57656         var today = new Date().clearTime().getTime();
57657         var sel = date.clearTime().getTime();
57658         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57659         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57660         var ddMatch = this.disabledDatesRE;
57661         var ddText = this.disabledDatesText;
57662         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57663         var ddaysText = this.disabledDaysText;
57664         var format = this.format;
57665         
57666         var setCellClass = function(cal, cell){
57667             
57668             //Roo.log('set Cell Class');
57669             cell.title = "";
57670             var t = d.getTime();
57671             
57672             //Roo.log(d);
57673             
57674             
57675             cell.dateValue = t;
57676             if(t == today){
57677                 cell.className += " fc-today";
57678                 cell.className += " fc-state-highlight";
57679                 cell.title = cal.todayText;
57680             }
57681             if(t == sel){
57682                 // disable highlight in other month..
57683                 cell.className += " fc-state-highlight";
57684                 
57685             }
57686             // disabling
57687             if(t < min) {
57688                 //cell.className = " fc-state-disabled";
57689                 cell.title = cal.minText;
57690                 return;
57691             }
57692             if(t > max) {
57693                 //cell.className = " fc-state-disabled";
57694                 cell.title = cal.maxText;
57695                 return;
57696             }
57697             if(ddays){
57698                 if(ddays.indexOf(d.getDay()) != -1){
57699                     // cell.title = ddaysText;
57700                    // cell.className = " fc-state-disabled";
57701                 }
57702             }
57703             if(ddMatch && format){
57704                 var fvalue = d.dateFormat(format);
57705                 if(ddMatch.test(fvalue)){
57706                     cell.title = ddText.replace("%0", fvalue);
57707                    cell.className = " fc-state-disabled";
57708                 }
57709             }
57710             
57711             if (!cell.initialClassName) {
57712                 cell.initialClassName = cell.dom.className;
57713             }
57714             
57715             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57716         };
57717
57718         var i = 0;
57719         
57720         for(; i < startingPos; i++) {
57721             cells[i].dayName =  (++prevStart);
57722             Roo.log(textEls[i]);
57723             d.setDate(d.getDate()+1);
57724             
57725             //cells[i].className = "fc-past fc-other-month";
57726             setCellClass(this, cells[i]);
57727         }
57728         
57729         var intDay = 0;
57730         
57731         for(; i < days; i++){
57732             intDay = i - startingPos + 1;
57733             cells[i].dayName =  (intDay);
57734             d.setDate(d.getDate()+1);
57735             
57736             cells[i].className = ''; // "x-date-active";
57737             setCellClass(this, cells[i]);
57738         }
57739         var extraDays = 0;
57740         
57741         for(; i < 42; i++) {
57742             //textEls[i].innerHTML = (++extraDays);
57743             
57744             d.setDate(d.getDate()+1);
57745             cells[i].dayName = (++extraDays);
57746             cells[i].className = "fc-future fc-other-month";
57747             setCellClass(this, cells[i]);
57748         }
57749         
57750         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57751         
57752         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57753         
57754         // this will cause all the cells to mis
57755         var rows= [];
57756         var i =0;
57757         for (var r = 0;r < 6;r++) {
57758             for (var c =0;c < 7;c++) {
57759                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57760             }    
57761         }
57762         
57763         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57764         for(i=0;i<cells.length;i++) {
57765             
57766             this.cells.elements[i].dayName = cells[i].dayName ;
57767             this.cells.elements[i].className = cells[i].className;
57768             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57769             this.cells.elements[i].title = cells[i].title ;
57770             this.cells.elements[i].dateValue = cells[i].dateValue ;
57771         }
57772         
57773         
57774         
57775         
57776         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57777         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57778         
57779         ////if(totalRows != 6){
57780             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57781            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57782        // }
57783         
57784         this.fireEvent('monthchange', this, date);
57785         
57786         
57787     },
57788  /**
57789      * Returns the grid's SelectionModel.
57790      * @return {SelectionModel}
57791      */
57792     getSelectionModel : function(){
57793         if(!this.selModel){
57794             this.selModel = new Roo.grid.CellSelectionModel();
57795         }
57796         return this.selModel;
57797     },
57798
57799     load: function() {
57800         this.eventStore.load()
57801         
57802         
57803         
57804     },
57805     
57806     findCell : function(dt) {
57807         dt = dt.clearTime().getTime();
57808         var ret = false;
57809         this.cells.each(function(c){
57810             //Roo.log("check " +c.dateValue + '?=' + dt);
57811             if(c.dateValue == dt){
57812                 ret = c;
57813                 return false;
57814             }
57815             return true;
57816         });
57817         
57818         return ret;
57819     },
57820     
57821     findCells : function(rec) {
57822         var s = rec.data.start_dt.clone().clearTime().getTime();
57823        // Roo.log(s);
57824         var e= rec.data.end_dt.clone().clearTime().getTime();
57825        // Roo.log(e);
57826         var ret = [];
57827         this.cells.each(function(c){
57828              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57829             
57830             if(c.dateValue > e){
57831                 return ;
57832             }
57833             if(c.dateValue < s){
57834                 return ;
57835             }
57836             ret.push(c);
57837         });
57838         
57839         return ret;    
57840     },
57841     
57842     findBestRow: function(cells)
57843     {
57844         var ret = 0;
57845         
57846         for (var i =0 ; i < cells.length;i++) {
57847             ret  = Math.max(cells[i].rows || 0,ret);
57848         }
57849         return ret;
57850         
57851     },
57852     
57853     
57854     addItem : function(rec)
57855     {
57856         // look for vertical location slot in
57857         var cells = this.findCells(rec);
57858         
57859         rec.row = this.findBestRow(cells);
57860         
57861         // work out the location.
57862         
57863         var crow = false;
57864         var rows = [];
57865         for(var i =0; i < cells.length; i++) {
57866             if (!crow) {
57867                 crow = {
57868                     start : cells[i],
57869                     end :  cells[i]
57870                 };
57871                 continue;
57872             }
57873             if (crow.start.getY() == cells[i].getY()) {
57874                 // on same row.
57875                 crow.end = cells[i];
57876                 continue;
57877             }
57878             // different row.
57879             rows.push(crow);
57880             crow = {
57881                 start: cells[i],
57882                 end : cells[i]
57883             };
57884             
57885         }
57886         
57887         rows.push(crow);
57888         rec.els = [];
57889         rec.rows = rows;
57890         rec.cells = cells;
57891         for (var i = 0; i < cells.length;i++) {
57892             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57893             
57894         }
57895         
57896         
57897     },
57898     
57899     clearEvents: function() {
57900         
57901         if (!this.eventStore.getCount()) {
57902             return;
57903         }
57904         // reset number of rows in cells.
57905         Roo.each(this.cells.elements, function(c){
57906             c.rows = 0;
57907         });
57908         
57909         this.eventStore.each(function(e) {
57910             this.clearEvent(e);
57911         },this);
57912         
57913     },
57914     
57915     clearEvent : function(ev)
57916     {
57917         if (ev.els) {
57918             Roo.each(ev.els, function(el) {
57919                 el.un('mouseenter' ,this.onEventEnter, this);
57920                 el.un('mouseleave' ,this.onEventLeave, this);
57921                 el.remove();
57922             },this);
57923             ev.els = [];
57924         }
57925     },
57926     
57927     
57928     renderEvent : function(ev,ctr) {
57929         if (!ctr) {
57930              ctr = this.view.el.select('.fc-event-container',true).first();
57931         }
57932         
57933          
57934         this.clearEvent(ev);
57935             //code
57936        
57937         
57938         
57939         ev.els = [];
57940         var cells = ev.cells;
57941         var rows = ev.rows;
57942         this.fireEvent('eventrender', this, ev);
57943         
57944         for(var i =0; i < rows.length; i++) {
57945             
57946             cls = '';
57947             if (i == 0) {
57948                 cls += ' fc-event-start';
57949             }
57950             if ((i+1) == rows.length) {
57951                 cls += ' fc-event-end';
57952             }
57953             
57954             //Roo.log(ev.data);
57955             // how many rows should it span..
57956             var cg = this.eventTmpl.append(ctr,Roo.apply({
57957                 fccls : cls
57958                 
57959             }, ev.data) , true);
57960             
57961             
57962             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57963             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57964             cg.on('click', this.onEventClick, this, ev);
57965             
57966             ev.els.push(cg);
57967             
57968             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57969             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57970             //Roo.log(cg);
57971              
57972             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57973             cg.setWidth(ebox.right - sbox.x -2);
57974         }
57975     },
57976     
57977     renderEvents: function()
57978     {   
57979         // first make sure there is enough space..
57980         
57981         if (!this.eventTmpl) {
57982             this.eventTmpl = new Roo.Template(
57983                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57984                     '<div class="fc-event-inner">' +
57985                         '<span class="fc-event-time">{time}</span>' +
57986                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57987                     '</div>' +
57988                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57989                 '</div>'
57990             );
57991                 
57992         }
57993                
57994         
57995         
57996         this.cells.each(function(c) {
57997             //Roo.log(c.select('.fc-day-content div',true).first());
57998             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57999         });
58000         
58001         var ctr = this.view.el.select('.fc-event-container',true).first();
58002         
58003         var cls;
58004         this.eventStore.each(function(ev){
58005             
58006             this.renderEvent(ev);
58007              
58008              
58009         }, this);
58010         this.view.layout();
58011         
58012     },
58013     
58014     onEventEnter: function (e, el,event,d) {
58015         this.fireEvent('evententer', this, el, event);
58016     },
58017     
58018     onEventLeave: function (e, el,event,d) {
58019         this.fireEvent('eventleave', this, el, event);
58020     },
58021     
58022     onEventClick: function (e, el,event,d) {
58023         this.fireEvent('eventclick', this, el, event);
58024     },
58025     
58026     onMonthChange: function () {
58027         this.store.load();
58028     },
58029     
58030     onLoad: function () {
58031         
58032         //Roo.log('calendar onload');
58033 //         
58034         if(this.eventStore.getCount() > 0){
58035             
58036            
58037             
58038             this.eventStore.each(function(d){
58039                 
58040                 
58041                 // FIXME..
58042                 var add =   d.data;
58043                 if (typeof(add.end_dt) == 'undefined')  {
58044                     Roo.log("Missing End time in calendar data: ");
58045                     Roo.log(d);
58046                     return;
58047                 }
58048                 if (typeof(add.start_dt) == 'undefined')  {
58049                     Roo.log("Missing Start time in calendar data: ");
58050                     Roo.log(d);
58051                     return;
58052                 }
58053                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58054                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58055                 add.id = add.id || d.id;
58056                 add.title = add.title || '??';
58057                 
58058                 this.addItem(d);
58059                 
58060              
58061             },this);
58062         }
58063         
58064         this.renderEvents();
58065     }
58066     
58067
58068 });
58069 /*
58070  grid : {
58071                 xtype: 'Grid',
58072                 xns: Roo.grid,
58073                 listeners : {
58074                     render : function ()
58075                     {
58076                         _this.grid = this;
58077                         
58078                         if (!this.view.el.hasClass('course-timesheet')) {
58079                             this.view.el.addClass('course-timesheet');
58080                         }
58081                         if (this.tsStyle) {
58082                             this.ds.load({});
58083                             return; 
58084                         }
58085                         Roo.log('width');
58086                         Roo.log(_this.grid.view.el.getWidth());
58087                         
58088                         
58089                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58090                             '.course-timesheet .x-grid-row' : {
58091                                 height: '80px'
58092                             },
58093                             '.x-grid-row td' : {
58094                                 'vertical-align' : 0
58095                             },
58096                             '.course-edit-link' : {
58097                                 'color' : 'blue',
58098                                 'text-overflow' : 'ellipsis',
58099                                 'overflow' : 'hidden',
58100                                 'white-space' : 'nowrap',
58101                                 'cursor' : 'pointer'
58102                             },
58103                             '.sub-link' : {
58104                                 'color' : 'green'
58105                             },
58106                             '.de-act-sup-link' : {
58107                                 'color' : 'purple',
58108                                 'text-decoration' : 'line-through'
58109                             },
58110                             '.de-act-link' : {
58111                                 'color' : 'red',
58112                                 'text-decoration' : 'line-through'
58113                             },
58114                             '.course-timesheet .course-highlight' : {
58115                                 'border-top-style': 'dashed !important',
58116                                 'border-bottom-bottom': 'dashed !important'
58117                             },
58118                             '.course-timesheet .course-item' : {
58119                                 'font-family'   : 'tahoma, arial, helvetica',
58120                                 'font-size'     : '11px',
58121                                 'overflow'      : 'hidden',
58122                                 'padding-left'  : '10px',
58123                                 'padding-right' : '10px',
58124                                 'padding-top' : '10px' 
58125                             }
58126                             
58127                         }, Roo.id());
58128                                 this.ds.load({});
58129                     }
58130                 },
58131                 autoWidth : true,
58132                 monitorWindowResize : false,
58133                 cellrenderer : function(v,x,r)
58134                 {
58135                     return v;
58136                 },
58137                 sm : {
58138                     xtype: 'CellSelectionModel',
58139                     xns: Roo.grid
58140                 },
58141                 dataSource : {
58142                     xtype: 'Store',
58143                     xns: Roo.data,
58144                     listeners : {
58145                         beforeload : function (_self, options)
58146                         {
58147                             options.params = options.params || {};
58148                             options.params._month = _this.monthField.getValue();
58149                             options.params.limit = 9999;
58150                             options.params['sort'] = 'when_dt';    
58151                             options.params['dir'] = 'ASC';    
58152                             this.proxy.loadResponse = this.loadResponse;
58153                             Roo.log("load?");
58154                             //this.addColumns();
58155                         },
58156                         load : function (_self, records, options)
58157                         {
58158                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58159                                 // if you click on the translation.. you can edit it...
58160                                 var el = Roo.get(this);
58161                                 var id = el.dom.getAttribute('data-id');
58162                                 var d = el.dom.getAttribute('data-date');
58163                                 var t = el.dom.getAttribute('data-time');
58164                                 //var id = this.child('span').dom.textContent;
58165                                 
58166                                 //Roo.log(this);
58167                                 Pman.Dialog.CourseCalendar.show({
58168                                     id : id,
58169                                     when_d : d,
58170                                     when_t : t,
58171                                     productitem_active : id ? 1 : 0
58172                                 }, function() {
58173                                     _this.grid.ds.load({});
58174                                 });
58175                            
58176                            });
58177                            
58178                            _this.panel.fireEvent('resize', [ '', '' ]);
58179                         }
58180                     },
58181                     loadResponse : function(o, success, response){
58182                             // this is overridden on before load..
58183                             
58184                             Roo.log("our code?");       
58185                             //Roo.log(success);
58186                             //Roo.log(response)
58187                             delete this.activeRequest;
58188                             if(!success){
58189                                 this.fireEvent("loadexception", this, o, response);
58190                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58191                                 return;
58192                             }
58193                             var result;
58194                             try {
58195                                 result = o.reader.read(response);
58196                             }catch(e){
58197                                 Roo.log("load exception?");
58198                                 this.fireEvent("loadexception", this, o, response, e);
58199                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58200                                 return;
58201                             }
58202                             Roo.log("ready...");        
58203                             // loop through result.records;
58204                             // and set this.tdate[date] = [] << array of records..
58205                             _this.tdata  = {};
58206                             Roo.each(result.records, function(r){
58207                                 //Roo.log(r.data);
58208                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58209                                     _this.tdata[r.data.when_dt.format('j')] = [];
58210                                 }
58211                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58212                             });
58213                             
58214                             //Roo.log(_this.tdata);
58215                             
58216                             result.records = [];
58217                             result.totalRecords = 6;
58218                     
58219                             // let's generate some duumy records for the rows.
58220                             //var st = _this.dateField.getValue();
58221                             
58222                             // work out monday..
58223                             //st = st.add(Date.DAY, -1 * st.format('w'));
58224                             
58225                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58226                             
58227                             var firstOfMonth = date.getFirstDayOfMonth();
58228                             var days = date.getDaysInMonth();
58229                             var d = 1;
58230                             var firstAdded = false;
58231                             for (var i = 0; i < result.totalRecords ; i++) {
58232                                 //var d= st.add(Date.DAY, i);
58233                                 var row = {};
58234                                 var added = 0;
58235                                 for(var w = 0 ; w < 7 ; w++){
58236                                     if(!firstAdded && firstOfMonth != w){
58237                                         continue;
58238                                     }
58239                                     if(d > days){
58240                                         continue;
58241                                     }
58242                                     firstAdded = true;
58243                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58244                                     row['weekday'+w] = String.format(
58245                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58246                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58247                                                     d,
58248                                                     date.format('Y-m-')+dd
58249                                                 );
58250                                     added++;
58251                                     if(typeof(_this.tdata[d]) != 'undefined'){
58252                                         Roo.each(_this.tdata[d], function(r){
58253                                             var is_sub = '';
58254                                             var deactive = '';
58255                                             var id = r.id;
58256                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58257                                             if(r.parent_id*1>0){
58258                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58259                                                 id = r.parent_id;
58260                                             }
58261                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58262                                                 deactive = 'de-act-link';
58263                                             }
58264                                             
58265                                             row['weekday'+w] += String.format(
58266                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58267                                                     id, //0
58268                                                     r.product_id_name, //1
58269                                                     r.when_dt.format('h:ia'), //2
58270                                                     is_sub, //3
58271                                                     deactive, //4
58272                                                     desc // 5
58273                                             );
58274                                         });
58275                                     }
58276                                     d++;
58277                                 }
58278                                 
58279                                 // only do this if something added..
58280                                 if(added > 0){ 
58281                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58282                                 }
58283                                 
58284                                 
58285                                 // push it twice. (second one with an hour..
58286                                 
58287                             }
58288                             //Roo.log(result);
58289                             this.fireEvent("load", this, o, o.request.arg);
58290                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58291                         },
58292                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58293                     proxy : {
58294                         xtype: 'HttpProxy',
58295                         xns: Roo.data,
58296                         method : 'GET',
58297                         url : baseURL + '/Roo/Shop_course.php'
58298                     },
58299                     reader : {
58300                         xtype: 'JsonReader',
58301                         xns: Roo.data,
58302                         id : 'id',
58303                         fields : [
58304                             {
58305                                 'name': 'id',
58306                                 'type': 'int'
58307                             },
58308                             {
58309                                 'name': 'when_dt',
58310                                 'type': 'string'
58311                             },
58312                             {
58313                                 'name': 'end_dt',
58314                                 'type': 'string'
58315                             },
58316                             {
58317                                 'name': 'parent_id',
58318                                 'type': 'int'
58319                             },
58320                             {
58321                                 'name': 'product_id',
58322                                 'type': 'int'
58323                             },
58324                             {
58325                                 'name': 'productitem_id',
58326                                 'type': 'int'
58327                             },
58328                             {
58329                                 'name': 'guid',
58330                                 'type': 'int'
58331                             }
58332                         ]
58333                     }
58334                 },
58335                 toolbar : {
58336                     xtype: 'Toolbar',
58337                     xns: Roo,
58338                     items : [
58339                         {
58340                             xtype: 'Button',
58341                             xns: Roo.Toolbar,
58342                             listeners : {
58343                                 click : function (_self, e)
58344                                 {
58345                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58346                                     sd.setMonth(sd.getMonth()-1);
58347                                     _this.monthField.setValue(sd.format('Y-m-d'));
58348                                     _this.grid.ds.load({});
58349                                 }
58350                             },
58351                             text : "Back"
58352                         },
58353                         {
58354                             xtype: 'Separator',
58355                             xns: Roo.Toolbar
58356                         },
58357                         {
58358                             xtype: 'MonthField',
58359                             xns: Roo.form,
58360                             listeners : {
58361                                 render : function (_self)
58362                                 {
58363                                     _this.monthField = _self;
58364                                    // _this.monthField.set  today
58365                                 },
58366                                 select : function (combo, date)
58367                                 {
58368                                     _this.grid.ds.load({});
58369                                 }
58370                             },
58371                             value : (function() { return new Date(); })()
58372                         },
58373                         {
58374                             xtype: 'Separator',
58375                             xns: Roo.Toolbar
58376                         },
58377                         {
58378                             xtype: 'TextItem',
58379                             xns: Roo.Toolbar,
58380                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58381                         },
58382                         {
58383                             xtype: 'Fill',
58384                             xns: Roo.Toolbar
58385                         },
58386                         {
58387                             xtype: 'Button',
58388                             xns: Roo.Toolbar,
58389                             listeners : {
58390                                 click : function (_self, e)
58391                                 {
58392                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58393                                     sd.setMonth(sd.getMonth()+1);
58394                                     _this.monthField.setValue(sd.format('Y-m-d'));
58395                                     _this.grid.ds.load({});
58396                                 }
58397                             },
58398                             text : "Next"
58399                         }
58400                     ]
58401                 },
58402                  
58403             }
58404         };
58405         
58406         *//*
58407  * Based on:
58408  * Ext JS Library 1.1.1
58409  * Copyright(c) 2006-2007, Ext JS, LLC.
58410  *
58411  * Originally Released Under LGPL - original licence link has changed is not relivant.
58412  *
58413  * Fork - LGPL
58414  * <script type="text/javascript">
58415  */
58416  
58417 /**
58418  * @class Roo.LoadMask
58419  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58420  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58421  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58422  * element's UpdateManager load indicator and will be destroyed after the initial load.
58423  * @constructor
58424  * Create a new LoadMask
58425  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58426  * @param {Object} config The config object
58427  */
58428 Roo.LoadMask = function(el, config){
58429     this.el = Roo.get(el);
58430     Roo.apply(this, config);
58431     if(this.store){
58432         this.store.on('beforeload', this.onBeforeLoad, this);
58433         this.store.on('load', this.onLoad, this);
58434         this.store.on('loadexception', this.onLoadException, this);
58435         this.removeMask = false;
58436     }else{
58437         var um = this.el.getUpdateManager();
58438         um.showLoadIndicator = false; // disable the default indicator
58439         um.on('beforeupdate', this.onBeforeLoad, this);
58440         um.on('update', this.onLoad, this);
58441         um.on('failure', this.onLoad, this);
58442         this.removeMask = true;
58443     }
58444 };
58445
58446 Roo.LoadMask.prototype = {
58447     /**
58448      * @cfg {Boolean} removeMask
58449      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58450      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58451      */
58452     /**
58453      * @cfg {String} msg
58454      * The text to display in a centered loading message box (defaults to 'Loading...')
58455      */
58456     msg : 'Loading...',
58457     /**
58458      * @cfg {String} msgCls
58459      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58460      */
58461     msgCls : 'x-mask-loading',
58462
58463     /**
58464      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58465      * @type Boolean
58466      */
58467     disabled: false,
58468
58469     /**
58470      * Disables the mask to prevent it from being displayed
58471      */
58472     disable : function(){
58473        this.disabled = true;
58474     },
58475
58476     /**
58477      * Enables the mask so that it can be displayed
58478      */
58479     enable : function(){
58480         this.disabled = false;
58481     },
58482     
58483     onLoadException : function()
58484     {
58485         Roo.log(arguments);
58486         
58487         if (typeof(arguments[3]) != 'undefined') {
58488             Roo.MessageBox.alert("Error loading",arguments[3]);
58489         } 
58490         /*
58491         try {
58492             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58493                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58494             }   
58495         } catch(e) {
58496             
58497         }
58498         */
58499     
58500         
58501         
58502         this.el.unmask(this.removeMask);
58503     },
58504     // private
58505     onLoad : function()
58506     {
58507         this.el.unmask(this.removeMask);
58508     },
58509
58510     // private
58511     onBeforeLoad : function(){
58512         if(!this.disabled){
58513             this.el.mask(this.msg, this.msgCls);
58514         }
58515     },
58516
58517     // private
58518     destroy : function(){
58519         if(this.store){
58520             this.store.un('beforeload', this.onBeforeLoad, this);
58521             this.store.un('load', this.onLoad, this);
58522             this.store.un('loadexception', this.onLoadException, this);
58523         }else{
58524             var um = this.el.getUpdateManager();
58525             um.un('beforeupdate', this.onBeforeLoad, this);
58526             um.un('update', this.onLoad, this);
58527             um.un('failure', this.onLoad, this);
58528         }
58529     }
58530 };/*
58531  * Based on:
58532  * Ext JS Library 1.1.1
58533  * Copyright(c) 2006-2007, Ext JS, LLC.
58534  *
58535  * Originally Released Under LGPL - original licence link has changed is not relivant.
58536  *
58537  * Fork - LGPL
58538  * <script type="text/javascript">
58539  */
58540
58541
58542 /**
58543  * @class Roo.XTemplate
58544  * @extends Roo.Template
58545  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58546 <pre><code>
58547 var t = new Roo.XTemplate(
58548         '&lt;select name="{name}"&gt;',
58549                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58550         '&lt;/select&gt;'
58551 );
58552  
58553 // then append, applying the master template values
58554  </code></pre>
58555  *
58556  * Supported features:
58557  *
58558  *  Tags:
58559
58560 <pre><code>
58561       {a_variable} - output encoded.
58562       {a_variable.format:("Y-m-d")} - call a method on the variable
58563       {a_variable:raw} - unencoded output
58564       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58565       {a_variable:this.method_on_template(...)} - call a method on the template object.
58566  
58567 </code></pre>
58568  *  The tpl tag:
58569 <pre><code>
58570         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58571         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58572         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58573         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58574   
58575         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58576         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58577 </code></pre>
58578  *      
58579  */
58580 Roo.XTemplate = function()
58581 {
58582     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58583     if (this.html) {
58584         this.compile();
58585     }
58586 };
58587
58588
58589 Roo.extend(Roo.XTemplate, Roo.Template, {
58590
58591     /**
58592      * The various sub templates
58593      */
58594     tpls : false,
58595     /**
58596      *
58597      * basic tag replacing syntax
58598      * WORD:WORD()
58599      *
58600      * // you can fake an object call by doing this
58601      *  x.t:(test,tesT) 
58602      * 
58603      */
58604     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58605
58606     /**
58607      * compile the template
58608      *
58609      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58610      *
58611      */
58612     compile: function()
58613     {
58614         var s = this.html;
58615      
58616         s = ['<tpl>', s, '</tpl>'].join('');
58617     
58618         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58619             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58620             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58621             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58622             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58623             m,
58624             id     = 0,
58625             tpls   = [];
58626     
58627         while(true == !!(m = s.match(re))){
58628             var forMatch   = m[0].match(nameRe),
58629                 ifMatch   = m[0].match(ifRe),
58630                 execMatch   = m[0].match(execRe),
58631                 namedMatch   = m[0].match(namedRe),
58632                 
58633                 exp  = null, 
58634                 fn   = null,
58635                 exec = null,
58636                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58637                 
58638             if (ifMatch) {
58639                 // if - puts fn into test..
58640                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58641                 if(exp){
58642                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58643                 }
58644             }
58645             
58646             if (execMatch) {
58647                 // exec - calls a function... returns empty if true is  returned.
58648                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58649                 if(exp){
58650                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58651                 }
58652             }
58653             
58654             
58655             if (name) {
58656                 // for = 
58657                 switch(name){
58658                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58659                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58660                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58661                 }
58662             }
58663             var uid = namedMatch ? namedMatch[1] : id;
58664             
58665             
58666             tpls.push({
58667                 id:     namedMatch ? namedMatch[1] : id,
58668                 target: name,
58669                 exec:   exec,
58670                 test:   fn,
58671                 body:   m[1] || ''
58672             });
58673             if (namedMatch) {
58674                 s = s.replace(m[0], '');
58675             } else { 
58676                 s = s.replace(m[0], '{xtpl'+ id + '}');
58677             }
58678             ++id;
58679         }
58680         this.tpls = [];
58681         for(var i = tpls.length-1; i >= 0; --i){
58682             this.compileTpl(tpls[i]);
58683             this.tpls[tpls[i].id] = tpls[i];
58684         }
58685         this.master = tpls[tpls.length-1];
58686         return this;
58687     },
58688     /**
58689      * same as applyTemplate, except it's done to one of the subTemplates
58690      * when using named templates, you can do:
58691      *
58692      * var str = pl.applySubTemplate('your-name', values);
58693      *
58694      * 
58695      * @param {Number} id of the template
58696      * @param {Object} values to apply to template
58697      * @param {Object} parent (normaly the instance of this object)
58698      */
58699     applySubTemplate : function(id, values, parent)
58700     {
58701         
58702         
58703         var t = this.tpls[id];
58704         
58705         
58706         try { 
58707             if(t.test && !t.test.call(this, values, parent)){
58708                 return '';
58709             }
58710         } catch(e) {
58711             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58712             Roo.log(e.toString());
58713             Roo.log(t.test);
58714             return ''
58715         }
58716         try { 
58717             
58718             if(t.exec && t.exec.call(this, values, parent)){
58719                 return '';
58720             }
58721         } catch(e) {
58722             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58723             Roo.log(e.toString());
58724             Roo.log(t.exec);
58725             return ''
58726         }
58727         try {
58728             var vs = t.target ? t.target.call(this, values, parent) : values;
58729             parent = t.target ? values : parent;
58730             if(t.target && vs instanceof Array){
58731                 var buf = [];
58732                 for(var i = 0, len = vs.length; i < len; i++){
58733                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58734                 }
58735                 return buf.join('');
58736             }
58737             return t.compiled.call(this, vs, parent);
58738         } catch (e) {
58739             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58740             Roo.log(e.toString());
58741             Roo.log(t.compiled);
58742             return '';
58743         }
58744     },
58745
58746     compileTpl : function(tpl)
58747     {
58748         var fm = Roo.util.Format;
58749         var useF = this.disableFormats !== true;
58750         var sep = Roo.isGecko ? "+" : ",";
58751         var undef = function(str) {
58752             Roo.log("Property not found :"  + str);
58753             return '';
58754         };
58755         
58756         var fn = function(m, name, format, args)
58757         {
58758             //Roo.log(arguments);
58759             args = args ? args.replace(/\\'/g,"'") : args;
58760             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58761             if (typeof(format) == 'undefined') {
58762                 format= 'htmlEncode';
58763             }
58764             if (format == 'raw' ) {
58765                 format = false;
58766             }
58767             
58768             if(name.substr(0, 4) == 'xtpl'){
58769                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58770             }
58771             
58772             // build an array of options to determine if value is undefined..
58773             
58774             // basically get 'xxxx.yyyy' then do
58775             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58776             //    (function () { Roo.log("Property not found"); return ''; })() :
58777             //    ......
58778             
58779             var udef_ar = [];
58780             var lookfor = '';
58781             Roo.each(name.split('.'), function(st) {
58782                 lookfor += (lookfor.length ? '.': '') + st;
58783                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58784             });
58785             
58786             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58787             
58788             
58789             if(format && useF){
58790                 
58791                 args = args ? ',' + args : "";
58792                  
58793                 if(format.substr(0, 5) != "this."){
58794                     format = "fm." + format + '(';
58795                 }else{
58796                     format = 'this.call("'+ format.substr(5) + '", ';
58797                     args = ", values";
58798                 }
58799                 
58800                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58801             }
58802              
58803             if (args.length) {
58804                 // called with xxyx.yuu:(test,test)
58805                 // change to ()
58806                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58807             }
58808             // raw.. - :raw modifier..
58809             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58810             
58811         };
58812         var body;
58813         // branched to use + in gecko and [].join() in others
58814         if(Roo.isGecko){
58815             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58816                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58817                     "';};};";
58818         }else{
58819             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58820             body.push(tpl.body.replace(/(\r\n|\n)/g,
58821                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58822             body.push("'].join('');};};");
58823             body = body.join('');
58824         }
58825         
58826         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58827        
58828         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58829         eval(body);
58830         
58831         return this;
58832     },
58833
58834     applyTemplate : function(values){
58835         return this.master.compiled.call(this, values, {});
58836         //var s = this.subs;
58837     },
58838
58839     apply : function(){
58840         return this.applyTemplate.apply(this, arguments);
58841     }
58842
58843  });
58844
58845 Roo.XTemplate.from = function(el){
58846     el = Roo.getDom(el);
58847     return new Roo.XTemplate(el.value || el.innerHTML);
58848 };