roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         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.isBorderBox){
6588         cls.push('roo-border-box');
6589     }
6590     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6591         var p = bd.dom.parentNode;
6592         if(p){
6593             p.className += ' roo-strict';
6594         }
6595     }
6596     bd.addClass(cls.join(' '));
6597 });
6598
6599 /**
6600  * @class Roo.EventObject
6601  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6602  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6603  * Example:
6604  * <pre><code>
6605  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6606     e.preventDefault();
6607     var target = e.getTarget();
6608     ...
6609  }
6610  var myDiv = Roo.get("myDiv");
6611  myDiv.on("click", handleClick);
6612  //or
6613  Roo.EventManager.on("myDiv", 'click', handleClick);
6614  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6615  </code></pre>
6616  * @singleton
6617  */
6618 Roo.EventObject = function(){
6619     
6620     var E = Roo.lib.Event;
6621     
6622     // safari keypress events for special keys return bad keycodes
6623     var safariKeys = {
6624         63234 : 37, // left
6625         63235 : 39, // right
6626         63232 : 38, // up
6627         63233 : 40, // down
6628         63276 : 33, // page up
6629         63277 : 34, // page down
6630         63272 : 46, // delete
6631         63273 : 36, // home
6632         63275 : 35  // end
6633     };
6634
6635     // normalize button clicks
6636     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6637                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6638
6639     Roo.EventObjectImpl = function(e){
6640         if(e){
6641             this.setEvent(e.browserEvent || e);
6642         }
6643     };
6644     Roo.EventObjectImpl.prototype = {
6645         /**
6646          * Used to fix doc tools.
6647          * @scope Roo.EventObject.prototype
6648          */
6649             
6650
6651         
6652         
6653         /** The normal browser event */
6654         browserEvent : null,
6655         /** The button pressed in a mouse event */
6656         button : -1,
6657         /** True if the shift key was down during the event */
6658         shiftKey : false,
6659         /** True if the control key was down during the event */
6660         ctrlKey : false,
6661         /** True if the alt key was down during the event */
6662         altKey : false,
6663
6664         /** Key constant 
6665         * @type Number */
6666         BACKSPACE : 8,
6667         /** Key constant 
6668         * @type Number */
6669         TAB : 9,
6670         /** Key constant 
6671         * @type Number */
6672         RETURN : 13,
6673         /** Key constant 
6674         * @type Number */
6675         ENTER : 13,
6676         /** Key constant 
6677         * @type Number */
6678         SHIFT : 16,
6679         /** Key constant 
6680         * @type Number */
6681         CONTROL : 17,
6682         /** Key constant 
6683         * @type Number */
6684         ESC : 27,
6685         /** Key constant 
6686         * @type Number */
6687         SPACE : 32,
6688         /** Key constant 
6689         * @type Number */
6690         PAGEUP : 33,
6691         /** Key constant 
6692         * @type Number */
6693         PAGEDOWN : 34,
6694         /** Key constant 
6695         * @type Number */
6696         END : 35,
6697         /** Key constant 
6698         * @type Number */
6699         HOME : 36,
6700         /** Key constant 
6701         * @type Number */
6702         LEFT : 37,
6703         /** Key constant 
6704         * @type Number */
6705         UP : 38,
6706         /** Key constant 
6707         * @type Number */
6708         RIGHT : 39,
6709         /** Key constant 
6710         * @type Number */
6711         DOWN : 40,
6712         /** Key constant 
6713         * @type Number */
6714         DELETE : 46,
6715         /** Key constant 
6716         * @type Number */
6717         F5 : 116,
6718
6719            /** @private */
6720         setEvent : function(e){
6721             if(e == this || (e && e.browserEvent)){ // already wrapped
6722                 return e;
6723             }
6724             this.browserEvent = e;
6725             if(e){
6726                 // normalize buttons
6727                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6728                 if(e.type == 'click' && this.button == -1){
6729                     this.button = 0;
6730                 }
6731                 this.type = e.type;
6732                 this.shiftKey = e.shiftKey;
6733                 // mac metaKey behaves like ctrlKey
6734                 this.ctrlKey = e.ctrlKey || e.metaKey;
6735                 this.altKey = e.altKey;
6736                 // in getKey these will be normalized for the mac
6737                 this.keyCode = e.keyCode;
6738                 // keyup warnings on firefox.
6739                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6740                 // cache the target for the delayed and or buffered events
6741                 this.target = E.getTarget(e);
6742                 // same for XY
6743                 this.xy = E.getXY(e);
6744             }else{
6745                 this.button = -1;
6746                 this.shiftKey = false;
6747                 this.ctrlKey = false;
6748                 this.altKey = false;
6749                 this.keyCode = 0;
6750                 this.charCode =0;
6751                 this.target = null;
6752                 this.xy = [0, 0];
6753             }
6754             return this;
6755         },
6756
6757         /**
6758          * Stop the event (preventDefault and stopPropagation)
6759          */
6760         stopEvent : function(){
6761             if(this.browserEvent){
6762                 if(this.browserEvent.type == 'mousedown'){
6763                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6764                 }
6765                 E.stopEvent(this.browserEvent);
6766             }
6767         },
6768
6769         /**
6770          * Prevents the browsers default handling of the event.
6771          */
6772         preventDefault : function(){
6773             if(this.browserEvent){
6774                 E.preventDefault(this.browserEvent);
6775             }
6776         },
6777
6778         /** @private */
6779         isNavKeyPress : function(){
6780             var k = this.keyCode;
6781             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6782             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6783         },
6784
6785         isSpecialKey : function(){
6786             var k = this.keyCode;
6787             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6788             (k == 16) || (k == 17) ||
6789             (k >= 18 && k <= 20) ||
6790             (k >= 33 && k <= 35) ||
6791             (k >= 36 && k <= 39) ||
6792             (k >= 44 && k <= 45);
6793         },
6794         /**
6795          * Cancels bubbling of the event.
6796          */
6797         stopPropagation : function(){
6798             if(this.browserEvent){
6799                 if(this.type == 'mousedown'){
6800                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6801                 }
6802                 E.stopPropagation(this.browserEvent);
6803             }
6804         },
6805
6806         /**
6807          * Gets the key code for the event.
6808          * @return {Number}
6809          */
6810         getCharCode : function(){
6811             return this.charCode || this.keyCode;
6812         },
6813
6814         /**
6815          * Returns a normalized keyCode for the event.
6816          * @return {Number} The key code
6817          */
6818         getKey : function(){
6819             var k = this.keyCode || this.charCode;
6820             return Roo.isSafari ? (safariKeys[k] || k) : k;
6821         },
6822
6823         /**
6824          * Gets the x coordinate of the event.
6825          * @return {Number}
6826          */
6827         getPageX : function(){
6828             return this.xy[0];
6829         },
6830
6831         /**
6832          * Gets the y coordinate of the event.
6833          * @return {Number}
6834          */
6835         getPageY : function(){
6836             return this.xy[1];
6837         },
6838
6839         /**
6840          * Gets the time of the event.
6841          * @return {Number}
6842          */
6843         getTime : function(){
6844             if(this.browserEvent){
6845                 return E.getTime(this.browserEvent);
6846             }
6847             return null;
6848         },
6849
6850         /**
6851          * Gets the page coordinates of the event.
6852          * @return {Array} The xy values like [x, y]
6853          */
6854         getXY : function(){
6855             return this.xy;
6856         },
6857
6858         /**
6859          * Gets the target for the event.
6860          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6861          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6862                 search as a number or element (defaults to 10 || document.body)
6863          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6864          * @return {HTMLelement}
6865          */
6866         getTarget : function(selector, maxDepth, returnEl){
6867             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6868         },
6869         /**
6870          * Gets the related target.
6871          * @return {HTMLElement}
6872          */
6873         getRelatedTarget : function(){
6874             if(this.browserEvent){
6875                 return E.getRelatedTarget(this.browserEvent);
6876             }
6877             return null;
6878         },
6879
6880         /**
6881          * Normalizes mouse wheel delta across browsers
6882          * @return {Number} The delta
6883          */
6884         getWheelDelta : function(){
6885             var e = this.browserEvent;
6886             var delta = 0;
6887             if(e.wheelDelta){ /* IE/Opera. */
6888                 delta = e.wheelDelta/120;
6889             }else if(e.detail){ /* Mozilla case. */
6890                 delta = -e.detail/3;
6891             }
6892             return delta;
6893         },
6894
6895         /**
6896          * Returns true if the control, meta, shift or alt key was pressed during this event.
6897          * @return {Boolean}
6898          */
6899         hasModifier : function(){
6900             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6901         },
6902
6903         /**
6904          * Returns true if the target of this event equals el or is a child of el
6905          * @param {String/HTMLElement/Element} el
6906          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6907          * @return {Boolean}
6908          */
6909         within : function(el, related){
6910             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6911             return t && Roo.fly(el).contains(t);
6912         },
6913
6914         getPoint : function(){
6915             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6916         }
6917     };
6918
6919     return new Roo.EventObjectImpl();
6920 }();
6921             
6922     /*
6923  * Based on:
6924  * Ext JS Library 1.1.1
6925  * Copyright(c) 2006-2007, Ext JS, LLC.
6926  *
6927  * Originally Released Under LGPL - original licence link has changed is not relivant.
6928  *
6929  * Fork - LGPL
6930  * <script type="text/javascript">
6931  */
6932
6933  
6934 // was in Composite Element!??!?!
6935  
6936 (function(){
6937     var D = Roo.lib.Dom;
6938     var E = Roo.lib.Event;
6939     var A = Roo.lib.Anim;
6940
6941     // local style camelizing for speed
6942     var propCache = {};
6943     var camelRe = /(-[a-z])/gi;
6944     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6945     var view = document.defaultView;
6946
6947 /**
6948  * @class Roo.Element
6949  * Represents an Element in the DOM.<br><br>
6950  * Usage:<br>
6951 <pre><code>
6952 var el = Roo.get("my-div");
6953
6954 // or with getEl
6955 var el = getEl("my-div");
6956
6957 // or with a DOM element
6958 var el = Roo.get(myDivElement);
6959 </code></pre>
6960  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6961  * each call instead of constructing a new one.<br><br>
6962  * <b>Animations</b><br />
6963  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6964  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6965 <pre>
6966 Option    Default   Description
6967 --------- --------  ---------------------------------------------
6968 duration  .35       The duration of the animation in seconds
6969 easing    easeOut   The YUI easing method
6970 callback  none      A function to execute when the anim completes
6971 scope     this      The scope (this) of the callback function
6972 </pre>
6973 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6974 * manipulate the animation. Here's an example:
6975 <pre><code>
6976 var el = Roo.get("my-div");
6977
6978 // no animation
6979 el.setWidth(100);
6980
6981 // default animation
6982 el.setWidth(100, true);
6983
6984 // animation with some options set
6985 el.setWidth(100, {
6986     duration: 1,
6987     callback: this.foo,
6988     scope: this
6989 });
6990
6991 // using the "anim" property to get the Anim object
6992 var opt = {
6993     duration: 1,
6994     callback: this.foo,
6995     scope: this
6996 };
6997 el.setWidth(100, opt);
6998 ...
6999 if(opt.anim.isAnimated()){
7000     opt.anim.stop();
7001 }
7002 </code></pre>
7003 * <b> Composite (Collections of) Elements</b><br />
7004  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7005  * @constructor Create a new Element directly.
7006  * @param {String/HTMLElement} element
7007  * @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).
7008  */
7009     Roo.Element = function(element, forceNew){
7010         var dom = typeof element == "string" ?
7011                 document.getElementById(element) : element;
7012         if(!dom){ // invalid id/element
7013             return null;
7014         }
7015         var id = dom.id;
7016         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7017             return Roo.Element.cache[id];
7018         }
7019
7020         /**
7021          * The DOM element
7022          * @type HTMLElement
7023          */
7024         this.dom = dom;
7025
7026         /**
7027          * The DOM element ID
7028          * @type String
7029          */
7030         this.id = id || Roo.id(dom);
7031     };
7032
7033     var El = Roo.Element;
7034
7035     El.prototype = {
7036         /**
7037          * The element's default display mode  (defaults to "")
7038          * @type String
7039          */
7040         originalDisplay : "",
7041
7042         visibilityMode : 1,
7043         /**
7044          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7045          * @type String
7046          */
7047         defaultUnit : "px",
7048         
7049         /**
7050          * Sets the element's visibility mode. When setVisible() is called it
7051          * will use this to determine whether to set the visibility or the display property.
7052          * @param visMode Element.VISIBILITY or Element.DISPLAY
7053          * @return {Roo.Element} this
7054          */
7055         setVisibilityMode : function(visMode){
7056             this.visibilityMode = visMode;
7057             return this;
7058         },
7059         /**
7060          * Convenience method for setVisibilityMode(Element.DISPLAY)
7061          * @param {String} display (optional) What to set display to when visible
7062          * @return {Roo.Element} this
7063          */
7064         enableDisplayMode : function(display){
7065             this.setVisibilityMode(El.DISPLAY);
7066             if(typeof display != "undefined") this.originalDisplay = display;
7067             return this;
7068         },
7069
7070         /**
7071          * 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)
7072          * @param {String} selector The simple selector to test
7073          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7074                 search as a number or element (defaults to 10 || document.body)
7075          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7076          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7077          */
7078         findParent : function(simpleSelector, maxDepth, returnEl){
7079             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7080             maxDepth = maxDepth || 50;
7081             if(typeof maxDepth != "number"){
7082                 stopEl = Roo.getDom(maxDepth);
7083                 maxDepth = 10;
7084             }
7085             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7086                 if(dq.is(p, simpleSelector)){
7087                     return returnEl ? Roo.get(p) : p;
7088                 }
7089                 depth++;
7090                 p = p.parentNode;
7091             }
7092             return null;
7093         },
7094
7095
7096         /**
7097          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7098          * @param {String} selector The simple selector to test
7099          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7100                 search as a number or element (defaults to 10 || document.body)
7101          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7102          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7103          */
7104         findParentNode : function(simpleSelector, maxDepth, returnEl){
7105             var p = Roo.fly(this.dom.parentNode, '_internal');
7106             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7107         },
7108
7109         /**
7110          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7111          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7112          * @param {String} selector The simple selector to test
7113          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7114                 search as a number or element (defaults to 10 || document.body)
7115          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7116          */
7117         up : function(simpleSelector, maxDepth){
7118             return this.findParentNode(simpleSelector, maxDepth, true);
7119         },
7120
7121
7122
7123         /**
7124          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7125          * @param {String} selector The simple selector to test
7126          * @return {Boolean} True if this element matches the selector, else false
7127          */
7128         is : function(simpleSelector){
7129             return Roo.DomQuery.is(this.dom, simpleSelector);
7130         },
7131
7132         /**
7133          * Perform animation on this element.
7134          * @param {Object} args The YUI animation control args
7135          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7136          * @param {Function} onComplete (optional) Function to call when animation completes
7137          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7138          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7139          * @return {Roo.Element} this
7140          */
7141         animate : function(args, duration, onComplete, easing, animType){
7142             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7143             return this;
7144         },
7145
7146         /*
7147          * @private Internal animation call
7148          */
7149         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7150             animType = animType || 'run';
7151             opt = opt || {};
7152             var anim = Roo.lib.Anim[animType](
7153                 this.dom, args,
7154                 (opt.duration || defaultDur) || .35,
7155                 (opt.easing || defaultEase) || 'easeOut',
7156                 function(){
7157                     Roo.callback(cb, this);
7158                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7159                 },
7160                 this
7161             );
7162             opt.anim = anim;
7163             return anim;
7164         },
7165
7166         // private legacy anim prep
7167         preanim : function(a, i){
7168             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7169         },
7170
7171         /**
7172          * Removes worthless text nodes
7173          * @param {Boolean} forceReclean (optional) By default the element
7174          * keeps track if it has been cleaned already so
7175          * you can call this over and over. However, if you update the element and
7176          * need to force a reclean, you can pass true.
7177          */
7178         clean : function(forceReclean){
7179             if(this.isCleaned && forceReclean !== true){
7180                 return this;
7181             }
7182             var ns = /\S/;
7183             var d = this.dom, n = d.firstChild, ni = -1;
7184             while(n){
7185                 var nx = n.nextSibling;
7186                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7187                     d.removeChild(n);
7188                 }else{
7189                     n.nodeIndex = ++ni;
7190                 }
7191                 n = nx;
7192             }
7193             this.isCleaned = true;
7194             return this;
7195         },
7196
7197         // private
7198         calcOffsetsTo : function(el){
7199             el = Roo.get(el);
7200             var d = el.dom;
7201             var restorePos = false;
7202             if(el.getStyle('position') == 'static'){
7203                 el.position('relative');
7204                 restorePos = true;
7205             }
7206             var x = 0, y =0;
7207             var op = this.dom;
7208             while(op && op != d && op.tagName != 'HTML'){
7209                 x+= op.offsetLeft;
7210                 y+= op.offsetTop;
7211                 op = op.offsetParent;
7212             }
7213             if(restorePos){
7214                 el.position('static');
7215             }
7216             return [x, y];
7217         },
7218
7219         /**
7220          * Scrolls this element into view within the passed container.
7221          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7222          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7223          * @return {Roo.Element} this
7224          */
7225         scrollIntoView : function(container, hscroll){
7226             var c = Roo.getDom(container) || document.body;
7227             var el = this.dom;
7228
7229             var o = this.calcOffsetsTo(c),
7230                 l = o[0],
7231                 t = o[1],
7232                 b = t+el.offsetHeight,
7233                 r = l+el.offsetWidth;
7234
7235             var ch = c.clientHeight;
7236             var ct = parseInt(c.scrollTop, 10);
7237             var cl = parseInt(c.scrollLeft, 10);
7238             var cb = ct + ch;
7239             var cr = cl + c.clientWidth;
7240
7241             if(t < ct){
7242                 c.scrollTop = t;
7243             }else if(b > cb){
7244                 c.scrollTop = b-ch;
7245             }
7246
7247             if(hscroll !== false){
7248                 if(l < cl){
7249                     c.scrollLeft = l;
7250                 }else if(r > cr){
7251                     c.scrollLeft = r-c.clientWidth;
7252                 }
7253             }
7254             return this;
7255         },
7256
7257         // private
7258         scrollChildIntoView : function(child, hscroll){
7259             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7260         },
7261
7262         /**
7263          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7264          * the new height may not be available immediately.
7265          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7266          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7267          * @param {Function} onComplete (optional) Function to call when animation completes
7268          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7269          * @return {Roo.Element} this
7270          */
7271         autoHeight : function(animate, duration, onComplete, easing){
7272             var oldHeight = this.getHeight();
7273             this.clip();
7274             this.setHeight(1); // force clipping
7275             setTimeout(function(){
7276                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7277                 if(!animate){
7278                     this.setHeight(height);
7279                     this.unclip();
7280                     if(typeof onComplete == "function"){
7281                         onComplete();
7282                     }
7283                 }else{
7284                     this.setHeight(oldHeight); // restore original height
7285                     this.setHeight(height, animate, duration, function(){
7286                         this.unclip();
7287                         if(typeof onComplete == "function") onComplete();
7288                     }.createDelegate(this), easing);
7289                 }
7290             }.createDelegate(this), 0);
7291             return this;
7292         },
7293
7294         /**
7295          * Returns true if this element is an ancestor of the passed element
7296          * @param {HTMLElement/String} el The element to check
7297          * @return {Boolean} True if this element is an ancestor of el, else false
7298          */
7299         contains : function(el){
7300             if(!el){return false;}
7301             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7302         },
7303
7304         /**
7305          * Checks whether the element is currently visible using both visibility and display properties.
7306          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7307          * @return {Boolean} True if the element is currently visible, else false
7308          */
7309         isVisible : function(deep) {
7310             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7311             if(deep !== true || !vis){
7312                 return vis;
7313             }
7314             var p = this.dom.parentNode;
7315             while(p && p.tagName.toLowerCase() != "body"){
7316                 if(!Roo.fly(p, '_isVisible').isVisible()){
7317                     return false;
7318                 }
7319                 p = p.parentNode;
7320             }
7321             return true;
7322         },
7323
7324         /**
7325          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7326          * @param {String} selector The CSS selector
7327          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7328          * @return {CompositeElement/CompositeElementLite} The composite element
7329          */
7330         select : function(selector, unique){
7331             return El.select(selector, unique, this.dom);
7332         },
7333
7334         /**
7335          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7336          * @param {String} selector The CSS selector
7337          * @return {Array} An array of the matched nodes
7338          */
7339         query : function(selector, unique){
7340             return Roo.DomQuery.select(selector, this.dom);
7341         },
7342
7343         /**
7344          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7345          * @param {String} selector The CSS selector
7346          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7347          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7348          */
7349         child : function(selector, returnDom){
7350             var n = Roo.DomQuery.selectNode(selector, this.dom);
7351             return returnDom ? n : Roo.get(n);
7352         },
7353
7354         /**
7355          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7356          * @param {String} selector The CSS selector
7357          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7358          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7359          */
7360         down : function(selector, returnDom){
7361             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7362             return returnDom ? n : Roo.get(n);
7363         },
7364
7365         /**
7366          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7367          * @param {String} group The group the DD object is member of
7368          * @param {Object} config The DD config object
7369          * @param {Object} overrides An object containing methods to override/implement on the DD object
7370          * @return {Roo.dd.DD} The DD object
7371          */
7372         initDD : function(group, config, overrides){
7373             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7374             return Roo.apply(dd, overrides);
7375         },
7376
7377         /**
7378          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7379          * @param {String} group The group the DDProxy object is member of
7380          * @param {Object} config The DDProxy config object
7381          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7382          * @return {Roo.dd.DDProxy} The DDProxy object
7383          */
7384         initDDProxy : function(group, config, overrides){
7385             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7386             return Roo.apply(dd, overrides);
7387         },
7388
7389         /**
7390          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7391          * @param {String} group The group the DDTarget object is member of
7392          * @param {Object} config The DDTarget config object
7393          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7394          * @return {Roo.dd.DDTarget} The DDTarget object
7395          */
7396         initDDTarget : function(group, config, overrides){
7397             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7398             return Roo.apply(dd, overrides);
7399         },
7400
7401         /**
7402          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7403          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7404          * @param {Boolean} visible Whether the element is visible
7405          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7406          * @return {Roo.Element} this
7407          */
7408          setVisible : function(visible, animate){
7409             if(!animate || !A){
7410                 if(this.visibilityMode == El.DISPLAY){
7411                     this.setDisplayed(visible);
7412                 }else{
7413                     this.fixDisplay();
7414                     this.dom.style.visibility = visible ? "visible" : "hidden";
7415                 }
7416             }else{
7417                 // closure for composites
7418                 var dom = this.dom;
7419                 var visMode = this.visibilityMode;
7420                 if(visible){
7421                     this.setOpacity(.01);
7422                     this.setVisible(true);
7423                 }
7424                 this.anim({opacity: { to: (visible?1:0) }},
7425                       this.preanim(arguments, 1),
7426                       null, .35, 'easeIn', function(){
7427                          if(!visible){
7428                              if(visMode == El.DISPLAY){
7429                                  dom.style.display = "none";
7430                              }else{
7431                                  dom.style.visibility = "hidden";
7432                              }
7433                              Roo.get(dom).setOpacity(1);
7434                          }
7435                      });
7436             }
7437             return this;
7438         },
7439
7440         /**
7441          * Returns true if display is not "none"
7442          * @return {Boolean}
7443          */
7444         isDisplayed : function() {
7445             return this.getStyle("display") != "none";
7446         },
7447
7448         /**
7449          * Toggles the element's visibility or display, depending on visibility mode.
7450          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7451          * @return {Roo.Element} this
7452          */
7453         toggle : function(animate){
7454             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7455             return this;
7456         },
7457
7458         /**
7459          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7460          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7461          * @return {Roo.Element} this
7462          */
7463         setDisplayed : function(value) {
7464             if(typeof value == "boolean"){
7465                value = value ? this.originalDisplay : "none";
7466             }
7467             this.setStyle("display", value);
7468             return this;
7469         },
7470
7471         /**
7472          * Tries to focus the element. Any exceptions are caught and ignored.
7473          * @return {Roo.Element} this
7474          */
7475         focus : function() {
7476             try{
7477                 this.dom.focus();
7478             }catch(e){}
7479             return this;
7480         },
7481
7482         /**
7483          * Tries to blur the element. Any exceptions are caught and ignored.
7484          * @return {Roo.Element} this
7485          */
7486         blur : function() {
7487             try{
7488                 this.dom.blur();
7489             }catch(e){}
7490             return this;
7491         },
7492
7493         /**
7494          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7495          * @param {String/Array} className The CSS class to add, or an array of classes
7496          * @return {Roo.Element} this
7497          */
7498         addClass : function(className){
7499             if(className instanceof Array){
7500                 for(var i = 0, len = className.length; i < len; i++) {
7501                     this.addClass(className[i]);
7502                 }
7503             }else{
7504                 if(className && !this.hasClass(className)){
7505                     this.dom.className = this.dom.className + " " + className;
7506                 }
7507             }
7508             return this;
7509         },
7510
7511         /**
7512          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7513          * @param {String/Array} className The CSS class to add, or an array of classes
7514          * @return {Roo.Element} this
7515          */
7516         radioClass : function(className){
7517             var siblings = this.dom.parentNode.childNodes;
7518             for(var i = 0; i < siblings.length; i++) {
7519                 var s = siblings[i];
7520                 if(s.nodeType == 1){
7521                     Roo.get(s).removeClass(className);
7522                 }
7523             }
7524             this.addClass(className);
7525             return this;
7526         },
7527
7528         /**
7529          * Removes one or more CSS classes from the element.
7530          * @param {String/Array} className The CSS class to remove, or an array of classes
7531          * @return {Roo.Element} this
7532          */
7533         removeClass : function(className){
7534             if(!className || !this.dom.className){
7535                 return this;
7536             }
7537             if(className instanceof Array){
7538                 for(var i = 0, len = className.length; i < len; i++) {
7539                     this.removeClass(className[i]);
7540                 }
7541             }else{
7542                 if(this.hasClass(className)){
7543                     var re = this.classReCache[className];
7544                     if (!re) {
7545                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7546                        this.classReCache[className] = re;
7547                     }
7548                     this.dom.className =
7549                         this.dom.className.replace(re, " ");
7550                 }
7551             }
7552             return this;
7553         },
7554
7555         // private
7556         classReCache: {},
7557
7558         /**
7559          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7560          * @param {String} className The CSS class to toggle
7561          * @return {Roo.Element} this
7562          */
7563         toggleClass : function(className){
7564             if(this.hasClass(className)){
7565                 this.removeClass(className);
7566             }else{
7567                 this.addClass(className);
7568             }
7569             return this;
7570         },
7571
7572         /**
7573          * Checks if the specified CSS class exists on this element's DOM node.
7574          * @param {String} className The CSS class to check for
7575          * @return {Boolean} True if the class exists, else false
7576          */
7577         hasClass : function(className){
7578             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7579         },
7580
7581         /**
7582          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7583          * @param {String} oldClassName The CSS class to replace
7584          * @param {String} newClassName The replacement CSS class
7585          * @return {Roo.Element} this
7586          */
7587         replaceClass : function(oldClassName, newClassName){
7588             this.removeClass(oldClassName);
7589             this.addClass(newClassName);
7590             return this;
7591         },
7592
7593         /**
7594          * Returns an object with properties matching the styles requested.
7595          * For example, el.getStyles('color', 'font-size', 'width') might return
7596          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7597          * @param {String} style1 A style name
7598          * @param {String} style2 A style name
7599          * @param {String} etc.
7600          * @return {Object} The style object
7601          */
7602         getStyles : function(){
7603             var a = arguments, len = a.length, r = {};
7604             for(var i = 0; i < len; i++){
7605                 r[a[i]] = this.getStyle(a[i]);
7606             }
7607             return r;
7608         },
7609
7610         /**
7611          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7612          * @param {String} property The style property whose value is returned.
7613          * @return {String} The current value of the style property for this element.
7614          */
7615         getStyle : function(){
7616             return view && view.getComputedStyle ?
7617                 function(prop){
7618                     var el = this.dom, v, cs, camel;
7619                     if(prop == 'float'){
7620                         prop = "cssFloat";
7621                     }
7622                     if(el.style && (v = el.style[prop])){
7623                         return v;
7624                     }
7625                     if(cs = view.getComputedStyle(el, "")){
7626                         if(!(camel = propCache[prop])){
7627                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7628                         }
7629                         return cs[camel];
7630                     }
7631                     return null;
7632                 } :
7633                 function(prop){
7634                     var el = this.dom, v, cs, camel;
7635                     if(prop == 'opacity'){
7636                         if(typeof el.style.filter == 'string'){
7637                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7638                             if(m){
7639                                 var fv = parseFloat(m[1]);
7640                                 if(!isNaN(fv)){
7641                                     return fv ? fv / 100 : 0;
7642                                 }
7643                             }
7644                         }
7645                         return 1;
7646                     }else if(prop == 'float'){
7647                         prop = "styleFloat";
7648                     }
7649                     if(!(camel = propCache[prop])){
7650                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7651                     }
7652                     if(v = el.style[camel]){
7653                         return v;
7654                     }
7655                     if(cs = el.currentStyle){
7656                         return cs[camel];
7657                     }
7658                     return null;
7659                 };
7660         }(),
7661
7662         /**
7663          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7664          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7665          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7666          * @return {Roo.Element} this
7667          */
7668         setStyle : function(prop, value){
7669             if(typeof prop == "string"){
7670                 
7671                 if (prop == 'float') {
7672                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7673                     return this;
7674                 }
7675                 
7676                 var camel;
7677                 if(!(camel = propCache[prop])){
7678                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7679                 }
7680                 
7681                 if(camel == 'opacity') {
7682                     this.setOpacity(value);
7683                 }else{
7684                     this.dom.style[camel] = value;
7685                 }
7686             }else{
7687                 for(var style in prop){
7688                     if(typeof prop[style] != "function"){
7689                        this.setStyle(style, prop[style]);
7690                     }
7691                 }
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * More flexible version of {@link #setStyle} for setting style properties.
7698          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7699          * a function which returns such a specification.
7700          * @return {Roo.Element} this
7701          */
7702         applyStyles : function(style){
7703             Roo.DomHelper.applyStyles(this.dom, style);
7704             return this;
7705         },
7706
7707         /**
7708           * 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).
7709           * @return {Number} The X position of the element
7710           */
7711         getX : function(){
7712             return D.getX(this.dom);
7713         },
7714
7715         /**
7716           * 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).
7717           * @return {Number} The Y position of the element
7718           */
7719         getY : function(){
7720             return D.getY(this.dom);
7721         },
7722
7723         /**
7724           * 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).
7725           * @return {Array} The XY position of the element
7726           */
7727         getXY : function(){
7728             return D.getXY(this.dom);
7729         },
7730
7731         /**
7732          * 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).
7733          * @param {Number} The X position of the element
7734          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7735          * @return {Roo.Element} this
7736          */
7737         setX : function(x, animate){
7738             if(!animate || !A){
7739                 D.setX(this.dom, x);
7740             }else{
7741                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7742             }
7743             return this;
7744         },
7745
7746         /**
7747          * 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).
7748          * @param {Number} The Y position of the element
7749          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7750          * @return {Roo.Element} this
7751          */
7752         setY : function(y, animate){
7753             if(!animate || !A){
7754                 D.setY(this.dom, y);
7755             }else{
7756                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7757             }
7758             return this;
7759         },
7760
7761         /**
7762          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7763          * @param {String} left The left CSS property value
7764          * @return {Roo.Element} this
7765          */
7766         setLeft : function(left){
7767             this.setStyle("left", this.addUnits(left));
7768             return this;
7769         },
7770
7771         /**
7772          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7773          * @param {String} top The top CSS property value
7774          * @return {Roo.Element} this
7775          */
7776         setTop : function(top){
7777             this.setStyle("top", this.addUnits(top));
7778             return this;
7779         },
7780
7781         /**
7782          * Sets the element's CSS right style.
7783          * @param {String} right The right CSS property value
7784          * @return {Roo.Element} this
7785          */
7786         setRight : function(right){
7787             this.setStyle("right", this.addUnits(right));
7788             return this;
7789         },
7790
7791         /**
7792          * Sets the element's CSS bottom style.
7793          * @param {String} bottom The bottom CSS property value
7794          * @return {Roo.Element} this
7795          */
7796         setBottom : function(bottom){
7797             this.setStyle("bottom", this.addUnits(bottom));
7798             return this;
7799         },
7800
7801         /**
7802          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7803          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7804          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7805          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7806          * @return {Roo.Element} this
7807          */
7808         setXY : function(pos, animate){
7809             if(!animate || !A){
7810                 D.setXY(this.dom, pos);
7811             }else{
7812                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7813             }
7814             return this;
7815         },
7816
7817         /**
7818          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7819          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7820          * @param {Number} x X value for new position (coordinates are page-based)
7821          * @param {Number} y Y value for new position (coordinates are page-based)
7822          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7823          * @return {Roo.Element} this
7824          */
7825         setLocation : function(x, y, animate){
7826             this.setXY([x, y], this.preanim(arguments, 2));
7827             return this;
7828         },
7829
7830         /**
7831          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7832          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7833          * @param {Number} x X value for new position (coordinates are page-based)
7834          * @param {Number} y Y value for new position (coordinates are page-based)
7835          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         moveTo : function(x, y, animate){
7839             this.setXY([x, y], this.preanim(arguments, 2));
7840             return this;
7841         },
7842
7843         /**
7844          * Returns the region of the given element.
7845          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7846          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7847          */
7848         getRegion : function(){
7849             return D.getRegion(this.dom);
7850         },
7851
7852         /**
7853          * Returns the offset height of the element
7854          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7855          * @return {Number} The element's height
7856          */
7857         getHeight : function(contentHeight){
7858             var h = this.dom.offsetHeight || 0;
7859             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7860         },
7861
7862         /**
7863          * Returns the offset width of the element
7864          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7865          * @return {Number} The element's width
7866          */
7867         getWidth : function(contentWidth){
7868             var w = this.dom.offsetWidth || 0;
7869             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7870         },
7871
7872         /**
7873          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7874          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7875          * if a height has not been set using CSS.
7876          * @return {Number}
7877          */
7878         getComputedHeight : function(){
7879             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7880             if(!h){
7881                 h = parseInt(this.getStyle('height'), 10) || 0;
7882                 if(!this.isBorderBox()){
7883                     h += this.getFrameWidth('tb');
7884                 }
7885             }
7886             return h;
7887         },
7888
7889         /**
7890          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7891          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7892          * if a width has not been set using CSS.
7893          * @return {Number}
7894          */
7895         getComputedWidth : function(){
7896             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7897             if(!w){
7898                 w = parseInt(this.getStyle('width'), 10) || 0;
7899                 if(!this.isBorderBox()){
7900                     w += this.getFrameWidth('lr');
7901                 }
7902             }
7903             return w;
7904         },
7905
7906         /**
7907          * Returns the size of the element.
7908          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7909          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7910          */
7911         getSize : function(contentSize){
7912             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7913         },
7914
7915         /**
7916          * Returns the width and height of the viewport.
7917          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7918          */
7919         getViewSize : function(){
7920             var d = this.dom, doc = document, aw = 0, ah = 0;
7921             if(d == doc || d == doc.body){
7922                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7923             }else{
7924                 return {
7925                     width : d.clientWidth,
7926                     height: d.clientHeight
7927                 };
7928             }
7929         },
7930
7931         /**
7932          * Returns the value of the "value" attribute
7933          * @param {Boolean} asNumber true to parse the value as a number
7934          * @return {String/Number}
7935          */
7936         getValue : function(asNumber){
7937             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7938         },
7939
7940         // private
7941         adjustWidth : function(width){
7942             if(typeof width == "number"){
7943                 if(this.autoBoxAdjust && !this.isBorderBox()){
7944                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7945                 }
7946                 if(width < 0){
7947                     width = 0;
7948                 }
7949             }
7950             return width;
7951         },
7952
7953         // private
7954         adjustHeight : function(height){
7955             if(typeof height == "number"){
7956                if(this.autoBoxAdjust && !this.isBorderBox()){
7957                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7958                }
7959                if(height < 0){
7960                    height = 0;
7961                }
7962             }
7963             return height;
7964         },
7965
7966         /**
7967          * Set the width of the element
7968          * @param {Number} width The new width
7969          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7970          * @return {Roo.Element} this
7971          */
7972         setWidth : function(width, animate){
7973             width = this.adjustWidth(width);
7974             if(!animate || !A){
7975                 this.dom.style.width = this.addUnits(width);
7976             }else{
7977                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7978             }
7979             return this;
7980         },
7981
7982         /**
7983          * Set the height of the element
7984          * @param {Number} height The new height
7985          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7986          * @return {Roo.Element} this
7987          */
7988          setHeight : function(height, animate){
7989             height = this.adjustHeight(height);
7990             if(!animate || !A){
7991                 this.dom.style.height = this.addUnits(height);
7992             }else{
7993                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7994             }
7995             return this;
7996         },
7997
7998         /**
7999          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8000          * @param {Number} width The new width
8001          * @param {Number} height The new height
8002          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8003          * @return {Roo.Element} this
8004          */
8005          setSize : function(width, height, animate){
8006             if(typeof width == "object"){ // in case of object from getSize()
8007                 height = width.height; width = width.width;
8008             }
8009             width = this.adjustWidth(width); height = this.adjustHeight(height);
8010             if(!animate || !A){
8011                 this.dom.style.width = this.addUnits(width);
8012                 this.dom.style.height = this.addUnits(height);
8013             }else{
8014                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8015             }
8016             return this;
8017         },
8018
8019         /**
8020          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8021          * @param {Number} x X value for new position (coordinates are page-based)
8022          * @param {Number} y Y value for new position (coordinates are page-based)
8023          * @param {Number} width The new width
8024          * @param {Number} height The new height
8025          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8026          * @return {Roo.Element} this
8027          */
8028         setBounds : function(x, y, width, height, animate){
8029             if(!animate || !A){
8030                 this.setSize(width, height);
8031                 this.setLocation(x, y);
8032             }else{
8033                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8034                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8035                               this.preanim(arguments, 4), 'motion');
8036             }
8037             return this;
8038         },
8039
8040         /**
8041          * 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.
8042          * @param {Roo.lib.Region} region The region to fill
8043          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8044          * @return {Roo.Element} this
8045          */
8046         setRegion : function(region, animate){
8047             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8048             return this;
8049         },
8050
8051         /**
8052          * Appends an event handler
8053          *
8054          * @param {String}   eventName     The type of event to append
8055          * @param {Function} fn        The method the event invokes
8056          * @param {Object} scope       (optional) The scope (this object) of the fn
8057          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8058          */
8059         addListener : function(eventName, fn, scope, options){
8060             if (this.dom) {
8061                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8062             }
8063         },
8064
8065         /**
8066          * Removes an event handler from this element
8067          * @param {String} eventName the type of event to remove
8068          * @param {Function} fn the method the event invokes
8069          * @return {Roo.Element} this
8070          */
8071         removeListener : function(eventName, fn){
8072             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8073             return this;
8074         },
8075
8076         /**
8077          * Removes all previous added listeners from this element
8078          * @return {Roo.Element} this
8079          */
8080         removeAllListeners : function(){
8081             E.purgeElement(this.dom);
8082             return this;
8083         },
8084
8085         relayEvent : function(eventName, observable){
8086             this.on(eventName, function(e){
8087                 observable.fireEvent(eventName, e);
8088             });
8089         },
8090
8091         /**
8092          * Set the opacity of the element
8093          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8094          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8095          * @return {Roo.Element} this
8096          */
8097          setOpacity : function(opacity, animate){
8098             if(!animate || !A){
8099                 var s = this.dom.style;
8100                 if(Roo.isIE){
8101                     s.zoom = 1;
8102                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8103                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8104                 }else{
8105                     s.opacity = opacity;
8106                 }
8107             }else{
8108                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8109             }
8110             return this;
8111         },
8112
8113         /**
8114          * Gets the left X coordinate
8115          * @param {Boolean} local True to get the local css position instead of page coordinate
8116          * @return {Number}
8117          */
8118         getLeft : function(local){
8119             if(!local){
8120                 return this.getX();
8121             }else{
8122                 return parseInt(this.getStyle("left"), 10) || 0;
8123             }
8124         },
8125
8126         /**
8127          * Gets the right X coordinate of the element (element X position + element width)
8128          * @param {Boolean} local True to get the local css position instead of page coordinate
8129          * @return {Number}
8130          */
8131         getRight : function(local){
8132             if(!local){
8133                 return this.getX() + this.getWidth();
8134             }else{
8135                 return (this.getLeft(true) + this.getWidth()) || 0;
8136             }
8137         },
8138
8139         /**
8140          * Gets the top Y coordinate
8141          * @param {Boolean} local True to get the local css position instead of page coordinate
8142          * @return {Number}
8143          */
8144         getTop : function(local) {
8145             if(!local){
8146                 return this.getY();
8147             }else{
8148                 return parseInt(this.getStyle("top"), 10) || 0;
8149             }
8150         },
8151
8152         /**
8153          * Gets the bottom Y coordinate of the element (element Y position + element height)
8154          * @param {Boolean} local True to get the local css position instead of page coordinate
8155          * @return {Number}
8156          */
8157         getBottom : function(local){
8158             if(!local){
8159                 return this.getY() + this.getHeight();
8160             }else{
8161                 return (this.getTop(true) + this.getHeight()) || 0;
8162             }
8163         },
8164
8165         /**
8166         * Initializes positioning on this element. If a desired position is not passed, it will make the
8167         * the element positioned relative IF it is not already positioned.
8168         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8169         * @param {Number} zIndex (optional) The zIndex to apply
8170         * @param {Number} x (optional) Set the page X position
8171         * @param {Number} y (optional) Set the page Y position
8172         */
8173         position : function(pos, zIndex, x, y){
8174             if(!pos){
8175                if(this.getStyle('position') == 'static'){
8176                    this.setStyle('position', 'relative');
8177                }
8178             }else{
8179                 this.setStyle("position", pos);
8180             }
8181             if(zIndex){
8182                 this.setStyle("z-index", zIndex);
8183             }
8184             if(x !== undefined && y !== undefined){
8185                 this.setXY([x, y]);
8186             }else if(x !== undefined){
8187                 this.setX(x);
8188             }else if(y !== undefined){
8189                 this.setY(y);
8190             }
8191         },
8192
8193         /**
8194         * Clear positioning back to the default when the document was loaded
8195         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8196         * @return {Roo.Element} this
8197          */
8198         clearPositioning : function(value){
8199             value = value ||'';
8200             this.setStyle({
8201                 "left": value,
8202                 "right": value,
8203                 "top": value,
8204                 "bottom": value,
8205                 "z-index": "",
8206                 "position" : "static"
8207             });
8208             return this;
8209         },
8210
8211         /**
8212         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8213         * snapshot before performing an update and then restoring the element.
8214         * @return {Object}
8215         */
8216         getPositioning : function(){
8217             var l = this.getStyle("left");
8218             var t = this.getStyle("top");
8219             return {
8220                 "position" : this.getStyle("position"),
8221                 "left" : l,
8222                 "right" : l ? "" : this.getStyle("right"),
8223                 "top" : t,
8224                 "bottom" : t ? "" : this.getStyle("bottom"),
8225                 "z-index" : this.getStyle("z-index")
8226             };
8227         },
8228
8229         /**
8230          * Gets the width of the border(s) for the specified side(s)
8231          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8232          * passing lr would get the border (l)eft width + the border (r)ight width.
8233          * @return {Number} The width of the sides passed added together
8234          */
8235         getBorderWidth : function(side){
8236             return this.addStyles(side, El.borders);
8237         },
8238
8239         /**
8240          * Gets the width of the padding(s) for the specified side(s)
8241          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8242          * passing lr would get the padding (l)eft + the padding (r)ight.
8243          * @return {Number} The padding of the sides passed added together
8244          */
8245         getPadding : function(side){
8246             return this.addStyles(side, El.paddings);
8247         },
8248
8249         /**
8250         * Set positioning with an object returned by getPositioning().
8251         * @param {Object} posCfg
8252         * @return {Roo.Element} this
8253          */
8254         setPositioning : function(pc){
8255             this.applyStyles(pc);
8256             if(pc.right == "auto"){
8257                 this.dom.style.right = "";
8258             }
8259             if(pc.bottom == "auto"){
8260                 this.dom.style.bottom = "";
8261             }
8262             return this;
8263         },
8264
8265         // private
8266         fixDisplay : function(){
8267             if(this.getStyle("display") == "none"){
8268                 this.setStyle("visibility", "hidden");
8269                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8270                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8271                     this.setStyle("display", "block");
8272                 }
8273             }
8274         },
8275
8276         /**
8277          * Quick set left and top adding default units
8278          * @param {String} left The left CSS property value
8279          * @param {String} top The top CSS property value
8280          * @return {Roo.Element} this
8281          */
8282          setLeftTop : function(left, top){
8283             this.dom.style.left = this.addUnits(left);
8284             this.dom.style.top = this.addUnits(top);
8285             return this;
8286         },
8287
8288         /**
8289          * Move this element relative to its current position.
8290          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8291          * @param {Number} distance How far to move the element in pixels
8292          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8293          * @return {Roo.Element} this
8294          */
8295          move : function(direction, distance, animate){
8296             var xy = this.getXY();
8297             direction = direction.toLowerCase();
8298             switch(direction){
8299                 case "l":
8300                 case "left":
8301                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8302                     break;
8303                case "r":
8304                case "right":
8305                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8306                     break;
8307                case "t":
8308                case "top":
8309                case "up":
8310                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8311                     break;
8312                case "b":
8313                case "bottom":
8314                case "down":
8315                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8316                     break;
8317             }
8318             return this;
8319         },
8320
8321         /**
8322          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8323          * @return {Roo.Element} this
8324          */
8325         clip : function(){
8326             if(!this.isClipped){
8327                this.isClipped = true;
8328                this.originalClip = {
8329                    "o": this.getStyle("overflow"),
8330                    "x": this.getStyle("overflow-x"),
8331                    "y": this.getStyle("overflow-y")
8332                };
8333                this.setStyle("overflow", "hidden");
8334                this.setStyle("overflow-x", "hidden");
8335                this.setStyle("overflow-y", "hidden");
8336             }
8337             return this;
8338         },
8339
8340         /**
8341          *  Return clipping (overflow) to original clipping before clip() was called
8342          * @return {Roo.Element} this
8343          */
8344         unclip : function(){
8345             if(this.isClipped){
8346                 this.isClipped = false;
8347                 var o = this.originalClip;
8348                 if(o.o){this.setStyle("overflow", o.o);}
8349                 if(o.x){this.setStyle("overflow-x", o.x);}
8350                 if(o.y){this.setStyle("overflow-y", o.y);}
8351             }
8352             return this;
8353         },
8354
8355
8356         /**
8357          * Gets the x,y coordinates specified by the anchor position on the element.
8358          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8359          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8360          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8361          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8362          * @return {Array} [x, y] An array containing the element's x and y coordinates
8363          */
8364         getAnchorXY : function(anchor, local, s){
8365             //Passing a different size is useful for pre-calculating anchors,
8366             //especially for anchored animations that change the el size.
8367
8368             var w, h, vp = false;
8369             if(!s){
8370                 var d = this.dom;
8371                 if(d == document.body || d == document){
8372                     vp = true;
8373                     w = D.getViewWidth(); h = D.getViewHeight();
8374                 }else{
8375                     w = this.getWidth(); h = this.getHeight();
8376                 }
8377             }else{
8378                 w = s.width;  h = s.height;
8379             }
8380             var x = 0, y = 0, r = Math.round;
8381             switch((anchor || "tl").toLowerCase()){
8382                 case "c":
8383                     x = r(w*.5);
8384                     y = r(h*.5);
8385                 break;
8386                 case "t":
8387                     x = r(w*.5);
8388                     y = 0;
8389                 break;
8390                 case "l":
8391                     x = 0;
8392                     y = r(h*.5);
8393                 break;
8394                 case "r":
8395                     x = w;
8396                     y = r(h*.5);
8397                 break;
8398                 case "b":
8399                     x = r(w*.5);
8400                     y = h;
8401                 break;
8402                 case "tl":
8403                     x = 0;
8404                     y = 0;
8405                 break;
8406                 case "bl":
8407                     x = 0;
8408                     y = h;
8409                 break;
8410                 case "br":
8411                     x = w;
8412                     y = h;
8413                 break;
8414                 case "tr":
8415                     x = w;
8416                     y = 0;
8417                 break;
8418             }
8419             if(local === true){
8420                 return [x, y];
8421             }
8422             if(vp){
8423                 var sc = this.getScroll();
8424                 return [x + sc.left, y + sc.top];
8425             }
8426             //Add the element's offset xy
8427             var o = this.getXY();
8428             return [x+o[0], y+o[1]];
8429         },
8430
8431         /**
8432          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8433          * supported position values.
8434          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8435          * @param {String} position The position to align to.
8436          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8437          * @return {Array} [x, y]
8438          */
8439         getAlignToXY : function(el, p, o){
8440             el = Roo.get(el);
8441             var d = this.dom;
8442             if(!el.dom){
8443                 throw "Element.alignTo with an element that doesn't exist";
8444             }
8445             var c = false; //constrain to viewport
8446             var p1 = "", p2 = "";
8447             o = o || [0,0];
8448
8449             if(!p){
8450                 p = "tl-bl";
8451             }else if(p == "?"){
8452                 p = "tl-bl?";
8453             }else if(p.indexOf("-") == -1){
8454                 p = "tl-" + p;
8455             }
8456             p = p.toLowerCase();
8457             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8458             if(!m){
8459                throw "Element.alignTo with an invalid alignment " + p;
8460             }
8461             p1 = m[1]; p2 = m[2]; c = !!m[3];
8462
8463             //Subtract the aligned el's internal xy from the target's offset xy
8464             //plus custom offset to get the aligned el's new offset xy
8465             var a1 = this.getAnchorXY(p1, true);
8466             var a2 = el.getAnchorXY(p2, false);
8467             var x = a2[0] - a1[0] + o[0];
8468             var y = a2[1] - a1[1] + o[1];
8469             if(c){
8470                 //constrain the aligned el to viewport if necessary
8471                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8472                 // 5px of margin for ie
8473                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8474
8475                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8476                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8477                 //otherwise swap the aligned el to the opposite border of the target.
8478                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8479                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8480                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8481                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8482
8483                var doc = document;
8484                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8485                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8486
8487                if((x+w) > dw + scrollX){
8488                     x = swapX ? r.left-w : dw+scrollX-w;
8489                 }
8490                if(x < scrollX){
8491                    x = swapX ? r.right : scrollX;
8492                }
8493                if((y+h) > dh + scrollY){
8494                     y = swapY ? r.top-h : dh+scrollY-h;
8495                 }
8496                if (y < scrollY){
8497                    y = swapY ? r.bottom : scrollY;
8498                }
8499             }
8500             return [x,y];
8501         },
8502
8503         // private
8504         getConstrainToXY : function(){
8505             var os = {top:0, left:0, bottom:0, right: 0};
8506
8507             return function(el, local, offsets, proposedXY){
8508                 el = Roo.get(el);
8509                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8510
8511                 var vw, vh, vx = 0, vy = 0;
8512                 if(el.dom == document.body || el.dom == document){
8513                     vw = Roo.lib.Dom.getViewWidth();
8514                     vh = Roo.lib.Dom.getViewHeight();
8515                 }else{
8516                     vw = el.dom.clientWidth;
8517                     vh = el.dom.clientHeight;
8518                     if(!local){
8519                         var vxy = el.getXY();
8520                         vx = vxy[0];
8521                         vy = vxy[1];
8522                     }
8523                 }
8524
8525                 var s = el.getScroll();
8526
8527                 vx += offsets.left + s.left;
8528                 vy += offsets.top + s.top;
8529
8530                 vw -= offsets.right;
8531                 vh -= offsets.bottom;
8532
8533                 var vr = vx+vw;
8534                 var vb = vy+vh;
8535
8536                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8537                 var x = xy[0], y = xy[1];
8538                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8539
8540                 // only move it if it needs it
8541                 var moved = false;
8542
8543                 // first validate right/bottom
8544                 if((x + w) > vr){
8545                     x = vr - w;
8546                     moved = true;
8547                 }
8548                 if((y + h) > vb){
8549                     y = vb - h;
8550                     moved = true;
8551                 }
8552                 // then make sure top/left isn't negative
8553                 if(x < vx){
8554                     x = vx;
8555                     moved = true;
8556                 }
8557                 if(y < vy){
8558                     y = vy;
8559                     moved = true;
8560                 }
8561                 return moved ? [x, y] : false;
8562             };
8563         }(),
8564
8565         // private
8566         adjustForConstraints : function(xy, parent, offsets){
8567             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8568         },
8569
8570         /**
8571          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8572          * document it aligns it to the viewport.
8573          * The position parameter is optional, and can be specified in any one of the following formats:
8574          * <ul>
8575          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8576          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8577          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8578          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8579          *   <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
8580          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8581          * </ul>
8582          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8583          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8584          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8585          * that specified in order to enforce the viewport constraints.
8586          * Following are all of the supported anchor positions:
8587     <pre>
8588     Value  Description
8589     -----  -----------------------------
8590     tl     The top left corner (default)
8591     t      The center of the top edge
8592     tr     The top right corner
8593     l      The center of the left edge
8594     c      In the center of the element
8595     r      The center of the right edge
8596     bl     The bottom left corner
8597     b      The center of the bottom edge
8598     br     The bottom right corner
8599     </pre>
8600     Example Usage:
8601     <pre><code>
8602     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8603     el.alignTo("other-el");
8604
8605     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8606     el.alignTo("other-el", "tr?");
8607
8608     // align the bottom right corner of el with the center left edge of other-el
8609     el.alignTo("other-el", "br-l?");
8610
8611     // align the center of el with the bottom left corner of other-el and
8612     // adjust the x position by -6 pixels (and the y position by 0)
8613     el.alignTo("other-el", "c-bl", [-6, 0]);
8614     </code></pre>
8615          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8616          * @param {String} position The position to align to.
8617          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8618          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8619          * @return {Roo.Element} this
8620          */
8621         alignTo : function(element, position, offsets, animate){
8622             var xy = this.getAlignToXY(element, position, offsets);
8623             this.setXY(xy, this.preanim(arguments, 3));
8624             return this;
8625         },
8626
8627         /**
8628          * Anchors an element to another element and realigns it when the window is resized.
8629          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8630          * @param {String} position The position to align to.
8631          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8632          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8633          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8634          * is a number, it is used as the buffer delay (defaults to 50ms).
8635          * @param {Function} callback The function to call after the animation finishes
8636          * @return {Roo.Element} this
8637          */
8638         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8639             var action = function(){
8640                 this.alignTo(el, alignment, offsets, animate);
8641                 Roo.callback(callback, this);
8642             };
8643             Roo.EventManager.onWindowResize(action, this);
8644             var tm = typeof monitorScroll;
8645             if(tm != 'undefined'){
8646                 Roo.EventManager.on(window, 'scroll', action, this,
8647                     {buffer: tm == 'number' ? monitorScroll : 50});
8648             }
8649             action.call(this); // align immediately
8650             return this;
8651         },
8652         /**
8653          * Clears any opacity settings from this element. Required in some cases for IE.
8654          * @return {Roo.Element} this
8655          */
8656         clearOpacity : function(){
8657             if (window.ActiveXObject) {
8658                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8659                     this.dom.style.filter = "";
8660                 }
8661             } else {
8662                 this.dom.style.opacity = "";
8663                 this.dom.style["-moz-opacity"] = "";
8664                 this.dom.style["-khtml-opacity"] = "";
8665             }
8666             return this;
8667         },
8668
8669         /**
8670          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8671          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8672          * @return {Roo.Element} this
8673          */
8674         hide : function(animate){
8675             this.setVisible(false, this.preanim(arguments, 0));
8676             return this;
8677         },
8678
8679         /**
8680         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8681         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8682          * @return {Roo.Element} this
8683          */
8684         show : function(animate){
8685             this.setVisible(true, this.preanim(arguments, 0));
8686             return this;
8687         },
8688
8689         /**
8690          * @private Test if size has a unit, otherwise appends the default
8691          */
8692         addUnits : function(size){
8693             return Roo.Element.addUnits(size, this.defaultUnit);
8694         },
8695
8696         /**
8697          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8698          * @return {Roo.Element} this
8699          */
8700         beginMeasure : function(){
8701             var el = this.dom;
8702             if(el.offsetWidth || el.offsetHeight){
8703                 return this; // offsets work already
8704             }
8705             var changed = [];
8706             var p = this.dom, b = document.body; // start with this element
8707             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8708                 var pe = Roo.get(p);
8709                 if(pe.getStyle('display') == 'none'){
8710                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8711                     p.style.visibility = "hidden";
8712                     p.style.display = "block";
8713                 }
8714                 p = p.parentNode;
8715             }
8716             this._measureChanged = changed;
8717             return this;
8718
8719         },
8720
8721         /**
8722          * Restores displays to before beginMeasure was called
8723          * @return {Roo.Element} this
8724          */
8725         endMeasure : function(){
8726             var changed = this._measureChanged;
8727             if(changed){
8728                 for(var i = 0, len = changed.length; i < len; i++) {
8729                     var r = changed[i];
8730                     r.el.style.visibility = r.visibility;
8731                     r.el.style.display = "none";
8732                 }
8733                 this._measureChanged = null;
8734             }
8735             return this;
8736         },
8737
8738         /**
8739         * Update the innerHTML of this element, optionally searching for and processing scripts
8740         * @param {String} html The new HTML
8741         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8742         * @param {Function} callback For async script loading you can be noticed when the update completes
8743         * @return {Roo.Element} this
8744          */
8745         update : function(html, loadScripts, callback){
8746             if(typeof html == "undefined"){
8747                 html = "";
8748             }
8749             if(loadScripts !== true){
8750                 this.dom.innerHTML = html;
8751                 if(typeof callback == "function"){
8752                     callback();
8753                 }
8754                 return this;
8755             }
8756             var id = Roo.id();
8757             var dom = this.dom;
8758
8759             html += '<span id="' + id + '"></span>';
8760
8761             E.onAvailable(id, function(){
8762                 var hd = document.getElementsByTagName("head")[0];
8763                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8764                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8765                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8766
8767                 var match;
8768                 while(match = re.exec(html)){
8769                     var attrs = match[1];
8770                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8771                     if(srcMatch && srcMatch[2]){
8772                        var s = document.createElement("script");
8773                        s.src = srcMatch[2];
8774                        var typeMatch = attrs.match(typeRe);
8775                        if(typeMatch && typeMatch[2]){
8776                            s.type = typeMatch[2];
8777                        }
8778                        hd.appendChild(s);
8779                     }else if(match[2] && match[2].length > 0){
8780                         if(window.execScript) {
8781                            window.execScript(match[2]);
8782                         } else {
8783                             /**
8784                              * eval:var:id
8785                              * eval:var:dom
8786                              * eval:var:html
8787                              * 
8788                              */
8789                            window.eval(match[2]);
8790                         }
8791                     }
8792                 }
8793                 var el = document.getElementById(id);
8794                 if(el){el.parentNode.removeChild(el);}
8795                 if(typeof callback == "function"){
8796                     callback();
8797                 }
8798             });
8799             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8800             return this;
8801         },
8802
8803         /**
8804          * Direct access to the UpdateManager update() method (takes the same parameters).
8805          * @param {String/Function} url The url for this request or a function to call to get the url
8806          * @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}
8807          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8808          * @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.
8809          * @return {Roo.Element} this
8810          */
8811         load : function(){
8812             var um = this.getUpdateManager();
8813             um.update.apply(um, arguments);
8814             return this;
8815         },
8816
8817         /**
8818         * Gets this element's UpdateManager
8819         * @return {Roo.UpdateManager} The UpdateManager
8820         */
8821         getUpdateManager : function(){
8822             if(!this.updateManager){
8823                 this.updateManager = new Roo.UpdateManager(this);
8824             }
8825             return this.updateManager;
8826         },
8827
8828         /**
8829          * Disables text selection for this element (normalized across browsers)
8830          * @return {Roo.Element} this
8831          */
8832         unselectable : function(){
8833             this.dom.unselectable = "on";
8834             this.swallowEvent("selectstart", true);
8835             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8836             this.addClass("x-unselectable");
8837             return this;
8838         },
8839
8840         /**
8841         * Calculates the x, y to center this element on the screen
8842         * @return {Array} The x, y values [x, y]
8843         */
8844         getCenterXY : function(){
8845             return this.getAlignToXY(document, 'c-c');
8846         },
8847
8848         /**
8849         * Centers the Element in either the viewport, or another Element.
8850         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8851         */
8852         center : function(centerIn){
8853             this.alignTo(centerIn || document, 'c-c');
8854             return this;
8855         },
8856
8857         /**
8858          * Tests various css rules/browsers to determine if this element uses a border box
8859          * @return {Boolean}
8860          */
8861         isBorderBox : function(){
8862             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8863         },
8864
8865         /**
8866          * Return a box {x, y, width, height} that can be used to set another elements
8867          * size/location to match this element.
8868          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8869          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8870          * @return {Object} box An object in the format {x, y, width, height}
8871          */
8872         getBox : function(contentBox, local){
8873             var xy;
8874             if(!local){
8875                 xy = this.getXY();
8876             }else{
8877                 var left = parseInt(this.getStyle("left"), 10) || 0;
8878                 var top = parseInt(this.getStyle("top"), 10) || 0;
8879                 xy = [left, top];
8880             }
8881             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8882             if(!contentBox){
8883                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8884             }else{
8885                 var l = this.getBorderWidth("l")+this.getPadding("l");
8886                 var r = this.getBorderWidth("r")+this.getPadding("r");
8887                 var t = this.getBorderWidth("t")+this.getPadding("t");
8888                 var b = this.getBorderWidth("b")+this.getPadding("b");
8889                 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)};
8890             }
8891             bx.right = bx.x + bx.width;
8892             bx.bottom = bx.y + bx.height;
8893             return bx;
8894         },
8895
8896         /**
8897          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8898          for more information about the sides.
8899          * @param {String} sides
8900          * @return {Number}
8901          */
8902         getFrameWidth : function(sides, onlyContentBox){
8903             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8904         },
8905
8906         /**
8907          * 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.
8908          * @param {Object} box The box to fill {x, y, width, height}
8909          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8910          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8911          * @return {Roo.Element} this
8912          */
8913         setBox : function(box, adjust, animate){
8914             var w = box.width, h = box.height;
8915             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8916                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8917                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8918             }
8919             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8920             return this;
8921         },
8922
8923         /**
8924          * Forces the browser to repaint this element
8925          * @return {Roo.Element} this
8926          */
8927          repaint : function(){
8928             var dom = this.dom;
8929             this.addClass("x-repaint");
8930             setTimeout(function(){
8931                 Roo.get(dom).removeClass("x-repaint");
8932             }, 1);
8933             return this;
8934         },
8935
8936         /**
8937          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8938          * then it returns the calculated width of the sides (see getPadding)
8939          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8940          * @return {Object/Number}
8941          */
8942         getMargins : function(side){
8943             if(!side){
8944                 return {
8945                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8946                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8947                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8948                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8949                 };
8950             }else{
8951                 return this.addStyles(side, El.margins);
8952              }
8953         },
8954
8955         // private
8956         addStyles : function(sides, styles){
8957             var val = 0, v, w;
8958             for(var i = 0, len = sides.length; i < len; i++){
8959                 v = this.getStyle(styles[sides.charAt(i)]);
8960                 if(v){
8961                      w = parseInt(v, 10);
8962                      if(w){ val += w; }
8963                 }
8964             }
8965             return val;
8966         },
8967
8968         /**
8969          * Creates a proxy element of this element
8970          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8971          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8972          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8973          * @return {Roo.Element} The new proxy element
8974          */
8975         createProxy : function(config, renderTo, matchBox){
8976             if(renderTo){
8977                 renderTo = Roo.getDom(renderTo);
8978             }else{
8979                 renderTo = document.body;
8980             }
8981             config = typeof config == "object" ?
8982                 config : {tag : "div", cls: config};
8983             var proxy = Roo.DomHelper.append(renderTo, config, true);
8984             if(matchBox){
8985                proxy.setBox(this.getBox());
8986             }
8987             return proxy;
8988         },
8989
8990         /**
8991          * Puts a mask over this element to disable user interaction. Requires core.css.
8992          * This method can only be applied to elements which accept child nodes.
8993          * @param {String} msg (optional) A message to display in the mask
8994          * @param {String} msgCls (optional) A css class to apply to the msg element
8995          * @return {Element} The mask  element
8996          */
8997         mask : function(msg, msgCls)
8998         {
8999             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9000                 this.setStyle("position", "relative");
9001             }
9002             if(!this._mask){
9003                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9004             }
9005             this.addClass("x-masked");
9006             this._mask.setDisplayed(true);
9007             
9008             // we wander
9009             var z = 0;
9010             var dom = this.dom
9011             while (dom && dom.style) {
9012                 if (!isNaN(parseInt(dom.style.zIndex))) {
9013                     z = Math.max(z, parseInt(dom.style.zIndex));
9014                 }
9015                 dom = dom.parentNode;
9016             }
9017             // if we are masking the body - then it hides everything..
9018             if (this.dom == document.body) {
9019                 z = 1000000;
9020                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9021                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9022             }
9023            
9024             if(typeof msg == 'string'){
9025                 if(!this._maskMsg){
9026                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9027                 }
9028                 var mm = this._maskMsg;
9029                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9030                 if (mm.dom.firstChild) { // weird IE issue?
9031                     mm.dom.firstChild.innerHTML = msg;
9032                 }
9033                 mm.setDisplayed(true);
9034                 mm.center(this);
9035                 mm.setStyle('z-index', z + 102);
9036             }
9037             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9038                 this._mask.setHeight(this.getHeight());
9039             }
9040             this._mask.setStyle('z-index', z + 100);
9041             
9042             return this._mask;
9043         },
9044
9045         /**
9046          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9047          * it is cached for reuse.
9048          */
9049         unmask : function(removeEl){
9050             if(this._mask){
9051                 if(removeEl === true){
9052                     this._mask.remove();
9053                     delete this._mask;
9054                     if(this._maskMsg){
9055                         this._maskMsg.remove();
9056                         delete this._maskMsg;
9057                     }
9058                 }else{
9059                     this._mask.setDisplayed(false);
9060                     if(this._maskMsg){
9061                         this._maskMsg.setDisplayed(false);
9062                     }
9063                 }
9064             }
9065             this.removeClass("x-masked");
9066         },
9067
9068         /**
9069          * Returns true if this element is masked
9070          * @return {Boolean}
9071          */
9072         isMasked : function(){
9073             return this._mask && this._mask.isVisible();
9074         },
9075
9076         /**
9077          * Creates an iframe shim for this element to keep selects and other windowed objects from
9078          * showing through.
9079          * @return {Roo.Element} The new shim element
9080          */
9081         createShim : function(){
9082             var el = document.createElement('iframe');
9083             el.frameBorder = 'no';
9084             el.className = 'roo-shim';
9085             if(Roo.isIE && Roo.isSecure){
9086                 el.src = Roo.SSL_SECURE_URL;
9087             }
9088             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9089             shim.autoBoxAdjust = false;
9090             return shim;
9091         },
9092
9093         /**
9094          * Removes this element from the DOM and deletes it from the cache
9095          */
9096         remove : function(){
9097             if(this.dom.parentNode){
9098                 this.dom.parentNode.removeChild(this.dom);
9099             }
9100             delete El.cache[this.dom.id];
9101         },
9102
9103         /**
9104          * Sets up event handlers to add and remove a css class when the mouse is over this element
9105          * @param {String} className
9106          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9107          * mouseout events for children elements
9108          * @return {Roo.Element} this
9109          */
9110         addClassOnOver : function(className, preventFlicker){
9111             this.on("mouseover", function(){
9112                 Roo.fly(this, '_internal').addClass(className);
9113             }, this.dom);
9114             var removeFn = function(e){
9115                 if(preventFlicker !== true || !e.within(this, true)){
9116                     Roo.fly(this, '_internal').removeClass(className);
9117                 }
9118             };
9119             this.on("mouseout", removeFn, this.dom);
9120             return this;
9121         },
9122
9123         /**
9124          * Sets up event handlers to add and remove a css class when this element has the focus
9125          * @param {String} className
9126          * @return {Roo.Element} this
9127          */
9128         addClassOnFocus : function(className){
9129             this.on("focus", function(){
9130                 Roo.fly(this, '_internal').addClass(className);
9131             }, this.dom);
9132             this.on("blur", function(){
9133                 Roo.fly(this, '_internal').removeClass(className);
9134             }, this.dom);
9135             return this;
9136         },
9137         /**
9138          * 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)
9139          * @param {String} className
9140          * @return {Roo.Element} this
9141          */
9142         addClassOnClick : function(className){
9143             var dom = this.dom;
9144             this.on("mousedown", function(){
9145                 Roo.fly(dom, '_internal').addClass(className);
9146                 var d = Roo.get(document);
9147                 var fn = function(){
9148                     Roo.fly(dom, '_internal').removeClass(className);
9149                     d.removeListener("mouseup", fn);
9150                 };
9151                 d.on("mouseup", fn);
9152             });
9153             return this;
9154         },
9155
9156         /**
9157          * Stops the specified event from bubbling and optionally prevents the default action
9158          * @param {String} eventName
9159          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9160          * @return {Roo.Element} this
9161          */
9162         swallowEvent : function(eventName, preventDefault){
9163             var fn = function(e){
9164                 e.stopPropagation();
9165                 if(preventDefault){
9166                     e.preventDefault();
9167                 }
9168             };
9169             if(eventName instanceof Array){
9170                 for(var i = 0, len = eventName.length; i < len; i++){
9171                      this.on(eventName[i], fn);
9172                 }
9173                 return this;
9174             }
9175             this.on(eventName, fn);
9176             return this;
9177         },
9178
9179         /**
9180          * @private
9181          */
9182       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9183
9184         /**
9185          * Sizes this element to its parent element's dimensions performing
9186          * neccessary box adjustments.
9187          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9188          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9189          * @return {Roo.Element} this
9190          */
9191         fitToParent : function(monitorResize, targetParent) {
9192           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9193           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9194           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9195             return;
9196           }
9197           var p = Roo.get(targetParent || this.dom.parentNode);
9198           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9199           if (monitorResize === true) {
9200             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9201             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9202           }
9203           return this;
9204         },
9205
9206         /**
9207          * Gets the next sibling, skipping text nodes
9208          * @return {HTMLElement} The next sibling or null
9209          */
9210         getNextSibling : function(){
9211             var n = this.dom.nextSibling;
9212             while(n && n.nodeType != 1){
9213                 n = n.nextSibling;
9214             }
9215             return n;
9216         },
9217
9218         /**
9219          * Gets the previous sibling, skipping text nodes
9220          * @return {HTMLElement} The previous sibling or null
9221          */
9222         getPrevSibling : function(){
9223             var n = this.dom.previousSibling;
9224             while(n && n.nodeType != 1){
9225                 n = n.previousSibling;
9226             }
9227             return n;
9228         },
9229
9230
9231         /**
9232          * Appends the passed element(s) to this element
9233          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9234          * @return {Roo.Element} this
9235          */
9236         appendChild: function(el){
9237             el = Roo.get(el);
9238             el.appendTo(this);
9239             return this;
9240         },
9241
9242         /**
9243          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9244          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9245          * automatically generated with the specified attributes.
9246          * @param {HTMLElement} insertBefore (optional) a child element of this element
9247          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9248          * @return {Roo.Element} The new child element
9249          */
9250         createChild: function(config, insertBefore, returnDom){
9251             config = config || {tag:'div'};
9252             if(insertBefore){
9253                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9254             }
9255             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9256         },
9257
9258         /**
9259          * Appends this element to the passed element
9260          * @param {String/HTMLElement/Element} el The new parent element
9261          * @return {Roo.Element} this
9262          */
9263         appendTo: function(el){
9264             el = Roo.getDom(el);
9265             el.appendChild(this.dom);
9266             return this;
9267         },
9268
9269         /**
9270          * Inserts this element before the passed element in the DOM
9271          * @param {String/HTMLElement/Element} el The element to insert before
9272          * @return {Roo.Element} this
9273          */
9274         insertBefore: function(el){
9275             el = Roo.getDom(el);
9276             el.parentNode.insertBefore(this.dom, el);
9277             return this;
9278         },
9279
9280         /**
9281          * Inserts this element after the passed element in the DOM
9282          * @param {String/HTMLElement/Element} el The element to insert after
9283          * @return {Roo.Element} this
9284          */
9285         insertAfter: function(el){
9286             el = Roo.getDom(el);
9287             el.parentNode.insertBefore(this.dom, el.nextSibling);
9288             return this;
9289         },
9290
9291         /**
9292          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9293          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9294          * @return {Roo.Element} The new child
9295          */
9296         insertFirst: function(el, returnDom){
9297             el = el || {};
9298             if(typeof el == 'object' && !el.nodeType){ // dh config
9299                 return this.createChild(el, this.dom.firstChild, returnDom);
9300             }else{
9301                 el = Roo.getDom(el);
9302                 this.dom.insertBefore(el, this.dom.firstChild);
9303                 return !returnDom ? Roo.get(el) : el;
9304             }
9305         },
9306
9307         /**
9308          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9309          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9310          * @param {String} where (optional) 'before' or 'after' defaults to before
9311          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9312          * @return {Roo.Element} the inserted Element
9313          */
9314         insertSibling: function(el, where, returnDom){
9315             where = where ? where.toLowerCase() : 'before';
9316             el = el || {};
9317             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9318
9319             if(typeof el == 'object' && !el.nodeType){ // dh config
9320                 if(where == 'after' && !this.dom.nextSibling){
9321                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9322                 }else{
9323                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9324                 }
9325
9326             }else{
9327                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9328                             where == 'before' ? this.dom : this.dom.nextSibling);
9329                 if(!returnDom){
9330                     rt = Roo.get(rt);
9331                 }
9332             }
9333             return rt;
9334         },
9335
9336         /**
9337          * Creates and wraps this element with another element
9338          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9339          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9340          * @return {HTMLElement/Element} The newly created wrapper element
9341          */
9342         wrap: function(config, returnDom){
9343             if(!config){
9344                 config = {tag: "div"};
9345             }
9346             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9347             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9348             return newEl;
9349         },
9350
9351         /**
9352          * Replaces the passed element with this element
9353          * @param {String/HTMLElement/Element} el The element to replace
9354          * @return {Roo.Element} this
9355          */
9356         replace: function(el){
9357             el = Roo.get(el);
9358             this.insertBefore(el);
9359             el.remove();
9360             return this;
9361         },
9362
9363         /**
9364          * Inserts an html fragment into this element
9365          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9366          * @param {String} html The HTML fragment
9367          * @param {Boolean} returnEl True to return an Roo.Element
9368          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9369          */
9370         insertHtml : function(where, html, returnEl){
9371             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9372             return returnEl ? Roo.get(el) : el;
9373         },
9374
9375         /**
9376          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9377          * @param {Object} o The object with the attributes
9378          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9379          * @return {Roo.Element} this
9380          */
9381         set : function(o, useSet){
9382             var el = this.dom;
9383             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9384             for(var attr in o){
9385                 if(attr == "style" || typeof o[attr] == "function") continue;
9386                 if(attr=="cls"){
9387                     el.className = o["cls"];
9388                 }else{
9389                     if(useSet) el.setAttribute(attr, o[attr]);
9390                     else el[attr] = o[attr];
9391                 }
9392             }
9393             if(o.style){
9394                 Roo.DomHelper.applyStyles(el, o.style);
9395             }
9396             return this;
9397         },
9398
9399         /**
9400          * Convenience method for constructing a KeyMap
9401          * @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:
9402          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9403          * @param {Function} fn The function to call
9404          * @param {Object} scope (optional) The scope of the function
9405          * @return {Roo.KeyMap} The KeyMap created
9406          */
9407         addKeyListener : function(key, fn, scope){
9408             var config;
9409             if(typeof key != "object" || key instanceof Array){
9410                 config = {
9411                     key: key,
9412                     fn: fn,
9413                     scope: scope
9414                 };
9415             }else{
9416                 config = {
9417                     key : key.key,
9418                     shift : key.shift,
9419                     ctrl : key.ctrl,
9420                     alt : key.alt,
9421                     fn: fn,
9422                     scope: scope
9423                 };
9424             }
9425             return new Roo.KeyMap(this, config);
9426         },
9427
9428         /**
9429          * Creates a KeyMap for this element
9430          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9431          * @return {Roo.KeyMap} The KeyMap created
9432          */
9433         addKeyMap : function(config){
9434             return new Roo.KeyMap(this, config);
9435         },
9436
9437         /**
9438          * Returns true if this element is scrollable.
9439          * @return {Boolean}
9440          */
9441          isScrollable : function(){
9442             var dom = this.dom;
9443             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9444         },
9445
9446         /**
9447          * 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().
9448          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9449          * @param {Number} value The new scroll value
9450          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9451          * @return {Element} this
9452          */
9453
9454         scrollTo : function(side, value, animate){
9455             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9456             if(!animate || !A){
9457                 this.dom[prop] = value;
9458             }else{
9459                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9460                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9461             }
9462             return this;
9463         },
9464
9465         /**
9466          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9467          * within this element's scrollable range.
9468          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9469          * @param {Number} distance How far to scroll the element in pixels
9470          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9471          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9472          * was scrolled as far as it could go.
9473          */
9474          scroll : function(direction, distance, animate){
9475              if(!this.isScrollable()){
9476                  return;
9477              }
9478              var el = this.dom;
9479              var l = el.scrollLeft, t = el.scrollTop;
9480              var w = el.scrollWidth, h = el.scrollHeight;
9481              var cw = el.clientWidth, ch = el.clientHeight;
9482              direction = direction.toLowerCase();
9483              var scrolled = false;
9484              var a = this.preanim(arguments, 2);
9485              switch(direction){
9486                  case "l":
9487                  case "left":
9488                      if(w - l > cw){
9489                          var v = Math.min(l + distance, w-cw);
9490                          this.scrollTo("left", v, a);
9491                          scrolled = true;
9492                      }
9493                      break;
9494                 case "r":
9495                 case "right":
9496                      if(l > 0){
9497                          var v = Math.max(l - distance, 0);
9498                          this.scrollTo("left", v, a);
9499                          scrolled = true;
9500                      }
9501                      break;
9502                 case "t":
9503                 case "top":
9504                 case "up":
9505                      if(t > 0){
9506                          var v = Math.max(t - distance, 0);
9507                          this.scrollTo("top", v, a);
9508                          scrolled = true;
9509                      }
9510                      break;
9511                 case "b":
9512                 case "bottom":
9513                 case "down":
9514                      if(h - t > ch){
9515                          var v = Math.min(t + distance, h-ch);
9516                          this.scrollTo("top", v, a);
9517                          scrolled = true;
9518                      }
9519                      break;
9520              }
9521              return scrolled;
9522         },
9523
9524         /**
9525          * Translates the passed page coordinates into left/top css values for this element
9526          * @param {Number/Array} x The page x or an array containing [x, y]
9527          * @param {Number} y The page y
9528          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9529          */
9530         translatePoints : function(x, y){
9531             if(typeof x == 'object' || x instanceof Array){
9532                 y = x[1]; x = x[0];
9533             }
9534             var p = this.getStyle('position');
9535             var o = this.getXY();
9536
9537             var l = parseInt(this.getStyle('left'), 10);
9538             var t = parseInt(this.getStyle('top'), 10);
9539
9540             if(isNaN(l)){
9541                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9542             }
9543             if(isNaN(t)){
9544                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9545             }
9546
9547             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9548         },
9549
9550         /**
9551          * Returns the current scroll position of the element.
9552          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9553          */
9554         getScroll : function(){
9555             var d = this.dom, doc = document;
9556             if(d == doc || d == doc.body){
9557                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9558                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9559                 return {left: l, top: t};
9560             }else{
9561                 return {left: d.scrollLeft, top: d.scrollTop};
9562             }
9563         },
9564
9565         /**
9566          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9567          * are convert to standard 6 digit hex color.
9568          * @param {String} attr The css attribute
9569          * @param {String} defaultValue The default value to use when a valid color isn't found
9570          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9571          * YUI color anims.
9572          */
9573         getColor : function(attr, defaultValue, prefix){
9574             var v = this.getStyle(attr);
9575             if(!v || v == "transparent" || v == "inherit") {
9576                 return defaultValue;
9577             }
9578             var color = typeof prefix == "undefined" ? "#" : prefix;
9579             if(v.substr(0, 4) == "rgb("){
9580                 var rvs = v.slice(4, v.length -1).split(",");
9581                 for(var i = 0; i < 3; i++){
9582                     var h = parseInt(rvs[i]).toString(16);
9583                     if(h < 16){
9584                         h = "0" + h;
9585                     }
9586                     color += h;
9587                 }
9588             } else {
9589                 if(v.substr(0, 1) == "#"){
9590                     if(v.length == 4) {
9591                         for(var i = 1; i < 4; i++){
9592                             var c = v.charAt(i);
9593                             color +=  c + c;
9594                         }
9595                     }else if(v.length == 7){
9596                         color += v.substr(1);
9597                     }
9598                 }
9599             }
9600             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9601         },
9602
9603         /**
9604          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9605          * gradient background, rounded corners and a 4-way shadow.
9606          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9607          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9608          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9609          * @return {Roo.Element} this
9610          */
9611         boxWrap : function(cls){
9612             cls = cls || 'x-box';
9613             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9614             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9615             return el;
9616         },
9617
9618         /**
9619          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9620          * @param {String} namespace The namespace in which to look for the attribute
9621          * @param {String} name The attribute name
9622          * @return {String} The attribute value
9623          */
9624         getAttributeNS : Roo.isIE ? function(ns, name){
9625             var d = this.dom;
9626             var type = typeof d[ns+":"+name];
9627             if(type != 'undefined' && type != 'unknown'){
9628                 return d[ns+":"+name];
9629             }
9630             return d[name];
9631         } : function(ns, name){
9632             var d = this.dom;
9633             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9634         },
9635         
9636         
9637         /**
9638          * Sets or Returns the value the dom attribute value
9639          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9640          * @param {String} value (optional) The value to set the attribute to
9641          * @return {String} The attribute value
9642          */
9643         attr : function(name){
9644             if (arguments.length > 1) {
9645                 this.dom.setAttribute(name, arguments[1]);
9646                 return arguments[1];
9647             }
9648             if (typeof(name) == 'object') {
9649                 for(var i in name) {
9650                     this.attr(i, name[i]);
9651                 }
9652                 return name;
9653             }
9654             
9655             
9656             if (!this.dom.hasAttribute(name)) {
9657                 return undefined;
9658             }
9659             return this.dom.getAttribute(name);
9660         }
9661         
9662         
9663         
9664     };
9665
9666     var ep = El.prototype;
9667
9668     /**
9669      * Appends an event handler (Shorthand for addListener)
9670      * @param {String}   eventName     The type of event to append
9671      * @param {Function} fn        The method the event invokes
9672      * @param {Object} scope       (optional) The scope (this object) of the fn
9673      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9674      * @method
9675      */
9676     ep.on = ep.addListener;
9677         // backwards compat
9678     ep.mon = ep.addListener;
9679
9680     /**
9681      * Removes an event handler from this element (shorthand for removeListener)
9682      * @param {String} eventName the type of event to remove
9683      * @param {Function} fn the method the event invokes
9684      * @return {Roo.Element} this
9685      * @method
9686      */
9687     ep.un = ep.removeListener;
9688
9689     /**
9690      * true to automatically adjust width and height settings for box-model issues (default to true)
9691      */
9692     ep.autoBoxAdjust = true;
9693
9694     // private
9695     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9696
9697     // private
9698     El.addUnits = function(v, defaultUnit){
9699         if(v === "" || v == "auto"){
9700             return v;
9701         }
9702         if(v === undefined){
9703             return '';
9704         }
9705         if(typeof v == "number" || !El.unitPattern.test(v)){
9706             return v + (defaultUnit || 'px');
9707         }
9708         return v;
9709     };
9710
9711     // special markup used throughout Roo when box wrapping elements
9712     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>';
9713     /**
9714      * Visibility mode constant - Use visibility to hide element
9715      * @static
9716      * @type Number
9717      */
9718     El.VISIBILITY = 1;
9719     /**
9720      * Visibility mode constant - Use display to hide element
9721      * @static
9722      * @type Number
9723      */
9724     El.DISPLAY = 2;
9725
9726     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9727     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9728     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9729
9730
9731
9732     /**
9733      * @private
9734      */
9735     El.cache = {};
9736
9737     var docEl;
9738
9739     /**
9740      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9741      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9742      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9743      * @return {Element} The Element object
9744      * @static
9745      */
9746     El.get = function(el){
9747         var ex, elm, id;
9748         if(!el){ return null; }
9749         if(typeof el == "string"){ // element id
9750             if(!(elm = document.getElementById(el))){
9751                 return null;
9752             }
9753             if(ex = El.cache[el]){
9754                 ex.dom = elm;
9755             }else{
9756                 ex = El.cache[el] = new El(elm);
9757             }
9758             return ex;
9759         }else if(el.tagName){ // dom element
9760             if(!(id = el.id)){
9761                 id = Roo.id(el);
9762             }
9763             if(ex = El.cache[id]){
9764                 ex.dom = el;
9765             }else{
9766                 ex = El.cache[id] = new El(el);
9767             }
9768             return ex;
9769         }else if(el instanceof El){
9770             if(el != docEl){
9771                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9772                                                               // catch case where it hasn't been appended
9773                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9774             }
9775             return el;
9776         }else if(el.isComposite){
9777             return el;
9778         }else if(el instanceof Array){
9779             return El.select(el);
9780         }else if(el == document){
9781             // create a bogus element object representing the document object
9782             if(!docEl){
9783                 var f = function(){};
9784                 f.prototype = El.prototype;
9785                 docEl = new f();
9786                 docEl.dom = document;
9787             }
9788             return docEl;
9789         }
9790         return null;
9791     };
9792
9793     // private
9794     El.uncache = function(el){
9795         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9796             if(a[i]){
9797                 delete El.cache[a[i].id || a[i]];
9798             }
9799         }
9800     };
9801
9802     // private
9803     // Garbage collection - uncache elements/purge listeners on orphaned elements
9804     // so we don't hold a reference and cause the browser to retain them
9805     El.garbageCollect = function(){
9806         if(!Roo.enableGarbageCollector){
9807             clearInterval(El.collectorThread);
9808             return;
9809         }
9810         for(var eid in El.cache){
9811             var el = El.cache[eid], d = el.dom;
9812             // -------------------------------------------------------
9813             // Determining what is garbage:
9814             // -------------------------------------------------------
9815             // !d
9816             // dom node is null, definitely garbage
9817             // -------------------------------------------------------
9818             // !d.parentNode
9819             // no parentNode == direct orphan, definitely garbage
9820             // -------------------------------------------------------
9821             // !d.offsetParent && !document.getElementById(eid)
9822             // display none elements have no offsetParent so we will
9823             // also try to look it up by it's id. However, check
9824             // offsetParent first so we don't do unneeded lookups.
9825             // This enables collection of elements that are not orphans
9826             // directly, but somewhere up the line they have an orphan
9827             // parent.
9828             // -------------------------------------------------------
9829             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9830                 delete El.cache[eid];
9831                 if(d && Roo.enableListenerCollection){
9832                     E.purgeElement(d);
9833                 }
9834             }
9835         }
9836     }
9837     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9838
9839
9840     // dom is optional
9841     El.Flyweight = function(dom){
9842         this.dom = dom;
9843     };
9844     El.Flyweight.prototype = El.prototype;
9845
9846     El._flyweights = {};
9847     /**
9848      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9849      * the dom node can be overwritten by other code.
9850      * @param {String/HTMLElement} el The dom node or id
9851      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9852      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9853      * @static
9854      * @return {Element} The shared Element object
9855      */
9856     El.fly = function(el, named){
9857         named = named || '_global';
9858         el = Roo.getDom(el);
9859         if(!el){
9860             return null;
9861         }
9862         if(!El._flyweights[named]){
9863             El._flyweights[named] = new El.Flyweight();
9864         }
9865         El._flyweights[named].dom = el;
9866         return El._flyweights[named];
9867     };
9868
9869     /**
9870      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9871      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9872      * Shorthand of {@link Roo.Element#get}
9873      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9874      * @return {Element} The Element object
9875      * @member Roo
9876      * @method get
9877      */
9878     Roo.get = El.get;
9879     /**
9880      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9881      * the dom node can be overwritten by other code.
9882      * Shorthand of {@link Roo.Element#fly}
9883      * @param {String/HTMLElement} el The dom node or id
9884      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9885      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9886      * @static
9887      * @return {Element} The shared Element object
9888      * @member Roo
9889      * @method fly
9890      */
9891     Roo.fly = El.fly;
9892
9893     // speedy lookup for elements never to box adjust
9894     var noBoxAdjust = Roo.isStrict ? {
9895         select:1
9896     } : {
9897         input:1, select:1, textarea:1
9898     };
9899     if(Roo.isIE || Roo.isGecko){
9900         noBoxAdjust['button'] = 1;
9901     }
9902
9903
9904     Roo.EventManager.on(window, 'unload', function(){
9905         delete El.cache;
9906         delete El._flyweights;
9907     });
9908 })();
9909
9910
9911
9912
9913 if(Roo.DomQuery){
9914     Roo.Element.selectorFunction = Roo.DomQuery.select;
9915 }
9916
9917 Roo.Element.select = function(selector, unique, root){
9918     var els;
9919     if(typeof selector == "string"){
9920         els = Roo.Element.selectorFunction(selector, root);
9921     }else if(selector.length !== undefined){
9922         els = selector;
9923     }else{
9924         throw "Invalid selector";
9925     }
9926     if(unique === true){
9927         return new Roo.CompositeElement(els);
9928     }else{
9929         return new Roo.CompositeElementLite(els);
9930     }
9931 };
9932 /**
9933  * Selects elements based on the passed CSS selector to enable working on them as 1.
9934  * @param {String/Array} selector The CSS selector or an array of elements
9935  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9936  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9937  * @return {CompositeElementLite/CompositeElement}
9938  * @member Roo
9939  * @method select
9940  */
9941 Roo.select = Roo.Element.select;
9942
9943
9944
9945
9946
9947
9948
9949
9950
9951
9952
9953
9954
9955
9956 /*
9957  * Based on:
9958  * Ext JS Library 1.1.1
9959  * Copyright(c) 2006-2007, Ext JS, LLC.
9960  *
9961  * Originally Released Under LGPL - original licence link has changed is not relivant.
9962  *
9963  * Fork - LGPL
9964  * <script type="text/javascript">
9965  */
9966
9967
9968
9969 //Notifies Element that fx methods are available
9970 Roo.enableFx = true;
9971
9972 /**
9973  * @class Roo.Fx
9974  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9975  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9976  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9977  * Element effects to work.</p><br/>
9978  *
9979  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9980  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9981  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9982  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9983  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9984  * expected results and should be done with care.</p><br/>
9985  *
9986  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9987  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9988 <pre>
9989 Value  Description
9990 -----  -----------------------------
9991 tl     The top left corner
9992 t      The center of the top edge
9993 tr     The top right corner
9994 l      The center of the left edge
9995 r      The center of the right edge
9996 bl     The bottom left corner
9997 b      The center of the bottom edge
9998 br     The bottom right corner
9999 </pre>
10000  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10001  * below are common options that can be passed to any Fx method.</b>
10002  * @cfg {Function} callback A function called when the effect is finished
10003  * @cfg {Object} scope The scope of the effect function
10004  * @cfg {String} easing A valid Easing value for the effect
10005  * @cfg {String} afterCls A css class to apply after the effect
10006  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10007  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10008  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10009  * effects that end with the element being visually hidden, ignored otherwise)
10010  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10011  * a function which returns such a specification that will be applied to the Element after the effect finishes
10012  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10013  * @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
10014  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10015  */
10016 Roo.Fx = {
10017         /**
10018          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10019          * origin for the slide effect.  This function automatically handles wrapping the element with
10020          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10021          * Usage:
10022          *<pre><code>
10023 // default: slide the element in from the top
10024 el.slideIn();
10025
10026 // custom: slide the element in from the right with a 2-second duration
10027 el.slideIn('r', { duration: 2 });
10028
10029 // common config options shown with default values
10030 el.slideIn('t', {
10031     easing: 'easeOut',
10032     duration: .5
10033 });
10034 </code></pre>
10035          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10036          * @param {Object} options (optional) Object literal with any of the Fx config options
10037          * @return {Roo.Element} The Element
10038          */
10039     slideIn : function(anchor, o){
10040         var el = this.getFxEl();
10041         o = o || {};
10042
10043         el.queueFx(o, function(){
10044
10045             anchor = anchor || "t";
10046
10047             // fix display to visibility
10048             this.fixDisplay();
10049
10050             // restore values after effect
10051             var r = this.getFxRestore();
10052             var b = this.getBox();
10053             // fixed size for slide
10054             this.setSize(b);
10055
10056             // wrap if needed
10057             var wrap = this.fxWrap(r.pos, o, "hidden");
10058
10059             var st = this.dom.style;
10060             st.visibility = "visible";
10061             st.position = "absolute";
10062
10063             // clear out temp styles after slide and unwrap
10064             var after = function(){
10065                 el.fxUnwrap(wrap, r.pos, o);
10066                 st.width = r.width;
10067                 st.height = r.height;
10068                 el.afterFx(o);
10069             };
10070             // time to calc the positions
10071             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10072
10073             switch(anchor.toLowerCase()){
10074                 case "t":
10075                     wrap.setSize(b.width, 0);
10076                     st.left = st.bottom = "0";
10077                     a = {height: bh};
10078                 break;
10079                 case "l":
10080                     wrap.setSize(0, b.height);
10081                     st.right = st.top = "0";
10082                     a = {width: bw};
10083                 break;
10084                 case "r":
10085                     wrap.setSize(0, b.height);
10086                     wrap.setX(b.right);
10087                     st.left = st.top = "0";
10088                     a = {width: bw, points: pt};
10089                 break;
10090                 case "b":
10091                     wrap.setSize(b.width, 0);
10092                     wrap.setY(b.bottom);
10093                     st.left = st.top = "0";
10094                     a = {height: bh, points: pt};
10095                 break;
10096                 case "tl":
10097                     wrap.setSize(0, 0);
10098                     st.right = st.bottom = "0";
10099                     a = {width: bw, height: bh};
10100                 break;
10101                 case "bl":
10102                     wrap.setSize(0, 0);
10103                     wrap.setY(b.y+b.height);
10104                     st.right = st.top = "0";
10105                     a = {width: bw, height: bh, points: pt};
10106                 break;
10107                 case "br":
10108                     wrap.setSize(0, 0);
10109                     wrap.setXY([b.right, b.bottom]);
10110                     st.left = st.top = "0";
10111                     a = {width: bw, height: bh, points: pt};
10112                 break;
10113                 case "tr":
10114                     wrap.setSize(0, 0);
10115                     wrap.setX(b.x+b.width);
10116                     st.left = st.bottom = "0";
10117                     a = {width: bw, height: bh, points: pt};
10118                 break;
10119             }
10120             this.dom.style.visibility = "visible";
10121             wrap.show();
10122
10123             arguments.callee.anim = wrap.fxanim(a,
10124                 o,
10125                 'motion',
10126                 .5,
10127                 'easeOut', after);
10128         });
10129         return this;
10130     },
10131     
10132         /**
10133          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10134          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10135          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10136          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10137          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10138          * Usage:
10139          *<pre><code>
10140 // default: slide the element out to the top
10141 el.slideOut();
10142
10143 // custom: slide the element out to the right with a 2-second duration
10144 el.slideOut('r', { duration: 2 });
10145
10146 // common config options shown with default values
10147 el.slideOut('t', {
10148     easing: 'easeOut',
10149     duration: .5,
10150     remove: false,
10151     useDisplay: false
10152 });
10153 </code></pre>
10154          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10155          * @param {Object} options (optional) Object literal with any of the Fx config options
10156          * @return {Roo.Element} The Element
10157          */
10158     slideOut : function(anchor, o){
10159         var el = this.getFxEl();
10160         o = o || {};
10161
10162         el.queueFx(o, function(){
10163
10164             anchor = anchor || "t";
10165
10166             // restore values after effect
10167             var r = this.getFxRestore();
10168             
10169             var b = this.getBox();
10170             // fixed size for slide
10171             this.setSize(b);
10172
10173             // wrap if needed
10174             var wrap = this.fxWrap(r.pos, o, "visible");
10175
10176             var st = this.dom.style;
10177             st.visibility = "visible";
10178             st.position = "absolute";
10179
10180             wrap.setSize(b);
10181
10182             var after = function(){
10183                 if(o.useDisplay){
10184                     el.setDisplayed(false);
10185                 }else{
10186                     el.hide();
10187                 }
10188
10189                 el.fxUnwrap(wrap, r.pos, o);
10190
10191                 st.width = r.width;
10192                 st.height = r.height;
10193
10194                 el.afterFx(o);
10195             };
10196
10197             var a, zero = {to: 0};
10198             switch(anchor.toLowerCase()){
10199                 case "t":
10200                     st.left = st.bottom = "0";
10201                     a = {height: zero};
10202                 break;
10203                 case "l":
10204                     st.right = st.top = "0";
10205                     a = {width: zero};
10206                 break;
10207                 case "r":
10208                     st.left = st.top = "0";
10209                     a = {width: zero, points: {to:[b.right, b.y]}};
10210                 break;
10211                 case "b":
10212                     st.left = st.top = "0";
10213                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10214                 break;
10215                 case "tl":
10216                     st.right = st.bottom = "0";
10217                     a = {width: zero, height: zero};
10218                 break;
10219                 case "bl":
10220                     st.right = st.top = "0";
10221                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10222                 break;
10223                 case "br":
10224                     st.left = st.top = "0";
10225                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10226                 break;
10227                 case "tr":
10228                     st.left = st.bottom = "0";
10229                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10230                 break;
10231             }
10232
10233             arguments.callee.anim = wrap.fxanim(a,
10234                 o,
10235                 'motion',
10236                 .5,
10237                 "easeOut", after);
10238         });
10239         return this;
10240     },
10241
10242         /**
10243          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10244          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10245          * The element must be removed from the DOM using the 'remove' config option if desired.
10246          * Usage:
10247          *<pre><code>
10248 // default
10249 el.puff();
10250
10251 // common config options shown with default values
10252 el.puff({
10253     easing: 'easeOut',
10254     duration: .5,
10255     remove: false,
10256     useDisplay: false
10257 });
10258 </code></pre>
10259          * @param {Object} options (optional) Object literal with any of the Fx config options
10260          * @return {Roo.Element} The Element
10261          */
10262     puff : function(o){
10263         var el = this.getFxEl();
10264         o = o || {};
10265
10266         el.queueFx(o, function(){
10267             this.clearOpacity();
10268             this.show();
10269
10270             // restore values after effect
10271             var r = this.getFxRestore();
10272             var st = this.dom.style;
10273
10274             var after = function(){
10275                 if(o.useDisplay){
10276                     el.setDisplayed(false);
10277                 }else{
10278                     el.hide();
10279                 }
10280
10281                 el.clearOpacity();
10282
10283                 el.setPositioning(r.pos);
10284                 st.width = r.width;
10285                 st.height = r.height;
10286                 st.fontSize = '';
10287                 el.afterFx(o);
10288             };
10289
10290             var width = this.getWidth();
10291             var height = this.getHeight();
10292
10293             arguments.callee.anim = this.fxanim({
10294                     width : {to: this.adjustWidth(width * 2)},
10295                     height : {to: this.adjustHeight(height * 2)},
10296                     points : {by: [-(width * .5), -(height * .5)]},
10297                     opacity : {to: 0},
10298                     fontSize: {to:200, unit: "%"}
10299                 },
10300                 o,
10301                 'motion',
10302                 .5,
10303                 "easeOut", after);
10304         });
10305         return this;
10306     },
10307
10308         /**
10309          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10310          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10311          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10312          * Usage:
10313          *<pre><code>
10314 // default
10315 el.switchOff();
10316
10317 // all config options shown with default values
10318 el.switchOff({
10319     easing: 'easeIn',
10320     duration: .3,
10321     remove: false,
10322     useDisplay: false
10323 });
10324 </code></pre>
10325          * @param {Object} options (optional) Object literal with any of the Fx config options
10326          * @return {Roo.Element} The Element
10327          */
10328     switchOff : function(o){
10329         var el = this.getFxEl();
10330         o = o || {};
10331
10332         el.queueFx(o, function(){
10333             this.clearOpacity();
10334             this.clip();
10335
10336             // restore values after effect
10337             var r = this.getFxRestore();
10338             var st = this.dom.style;
10339
10340             var after = function(){
10341                 if(o.useDisplay){
10342                     el.setDisplayed(false);
10343                 }else{
10344                     el.hide();
10345                 }
10346
10347                 el.clearOpacity();
10348                 el.setPositioning(r.pos);
10349                 st.width = r.width;
10350                 st.height = r.height;
10351
10352                 el.afterFx(o);
10353             };
10354
10355             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10356                 this.clearOpacity();
10357                 (function(){
10358                     this.fxanim({
10359                         height:{to:1},
10360                         points:{by:[0, this.getHeight() * .5]}
10361                     }, o, 'motion', 0.3, 'easeIn', after);
10362                 }).defer(100, this);
10363             });
10364         });
10365         return this;
10366     },
10367
10368     /**
10369      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10370      * changed using the "attr" config option) and then fading back to the original color. If no original
10371      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10372      * Usage:
10373 <pre><code>
10374 // default: highlight background to yellow
10375 el.highlight();
10376
10377 // custom: highlight foreground text to blue for 2 seconds
10378 el.highlight("0000ff", { attr: 'color', duration: 2 });
10379
10380 // common config options shown with default values
10381 el.highlight("ffff9c", {
10382     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10383     endColor: (current color) or "ffffff",
10384     easing: 'easeIn',
10385     duration: 1
10386 });
10387 </code></pre>
10388      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10389      * @param {Object} options (optional) Object literal with any of the Fx config options
10390      * @return {Roo.Element} The Element
10391      */ 
10392     highlight : function(color, o){
10393         var el = this.getFxEl();
10394         o = o || {};
10395
10396         el.queueFx(o, function(){
10397             color = color || "ffff9c";
10398             attr = o.attr || "backgroundColor";
10399
10400             this.clearOpacity();
10401             this.show();
10402
10403             var origColor = this.getColor(attr);
10404             var restoreColor = this.dom.style[attr];
10405             endColor = (o.endColor || origColor) || "ffffff";
10406
10407             var after = function(){
10408                 el.dom.style[attr] = restoreColor;
10409                 el.afterFx(o);
10410             };
10411
10412             var a = {};
10413             a[attr] = {from: color, to: endColor};
10414             arguments.callee.anim = this.fxanim(a,
10415                 o,
10416                 'color',
10417                 1,
10418                 'easeIn', after);
10419         });
10420         return this;
10421     },
10422
10423    /**
10424     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10425     * Usage:
10426 <pre><code>
10427 // default: a single light blue ripple
10428 el.frame();
10429
10430 // custom: 3 red ripples lasting 3 seconds total
10431 el.frame("ff0000", 3, { duration: 3 });
10432
10433 // common config options shown with default values
10434 el.frame("C3DAF9", 1, {
10435     duration: 1 //duration of entire animation (not each individual ripple)
10436     // Note: Easing is not configurable and will be ignored if included
10437 });
10438 </code></pre>
10439     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10440     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10441     * @param {Object} options (optional) Object literal with any of the Fx config options
10442     * @return {Roo.Element} The Element
10443     */
10444     frame : function(color, count, o){
10445         var el = this.getFxEl();
10446         o = o || {};
10447
10448         el.queueFx(o, function(){
10449             color = color || "#C3DAF9";
10450             if(color.length == 6){
10451                 color = "#" + color;
10452             }
10453             count = count || 1;
10454             duration = o.duration || 1;
10455             this.show();
10456
10457             var b = this.getBox();
10458             var animFn = function(){
10459                 var proxy = this.createProxy({
10460
10461                      style:{
10462                         visbility:"hidden",
10463                         position:"absolute",
10464                         "z-index":"35000", // yee haw
10465                         border:"0px solid " + color
10466                      }
10467                   });
10468                 var scale = Roo.isBorderBox ? 2 : 1;
10469                 proxy.animate({
10470                     top:{from:b.y, to:b.y - 20},
10471                     left:{from:b.x, to:b.x - 20},
10472                     borderWidth:{from:0, to:10},
10473                     opacity:{from:1, to:0},
10474                     height:{from:b.height, to:(b.height + (20*scale))},
10475                     width:{from:b.width, to:(b.width + (20*scale))}
10476                 }, duration, function(){
10477                     proxy.remove();
10478                 });
10479                 if(--count > 0){
10480                      animFn.defer((duration/2)*1000, this);
10481                 }else{
10482                     el.afterFx(o);
10483                 }
10484             };
10485             animFn.call(this);
10486         });
10487         return this;
10488     },
10489
10490    /**
10491     * Creates a pause before any subsequent queued effects begin.  If there are
10492     * no effects queued after the pause it will have no effect.
10493     * Usage:
10494 <pre><code>
10495 el.pause(1);
10496 </code></pre>
10497     * @param {Number} seconds The length of time to pause (in seconds)
10498     * @return {Roo.Element} The Element
10499     */
10500     pause : function(seconds){
10501         var el = this.getFxEl();
10502         var o = {};
10503
10504         el.queueFx(o, function(){
10505             setTimeout(function(){
10506                 el.afterFx(o);
10507             }, seconds * 1000);
10508         });
10509         return this;
10510     },
10511
10512    /**
10513     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10514     * using the "endOpacity" config option.
10515     * Usage:
10516 <pre><code>
10517 // default: fade in from opacity 0 to 100%
10518 el.fadeIn();
10519
10520 // custom: fade in from opacity 0 to 75% over 2 seconds
10521 el.fadeIn({ endOpacity: .75, duration: 2});
10522
10523 // common config options shown with default values
10524 el.fadeIn({
10525     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10526     easing: 'easeOut',
10527     duration: .5
10528 });
10529 </code></pre>
10530     * @param {Object} options (optional) Object literal with any of the Fx config options
10531     * @return {Roo.Element} The Element
10532     */
10533     fadeIn : function(o){
10534         var el = this.getFxEl();
10535         o = o || {};
10536         el.queueFx(o, function(){
10537             this.setOpacity(0);
10538             this.fixDisplay();
10539             this.dom.style.visibility = 'visible';
10540             var to = o.endOpacity || 1;
10541             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10542                 o, null, .5, "easeOut", function(){
10543                 if(to == 1){
10544                     this.clearOpacity();
10545                 }
10546                 el.afterFx(o);
10547             });
10548         });
10549         return this;
10550     },
10551
10552    /**
10553     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10554     * using the "endOpacity" config option.
10555     * Usage:
10556 <pre><code>
10557 // default: fade out from the element's current opacity to 0
10558 el.fadeOut();
10559
10560 // custom: fade out from the element's current opacity to 25% over 2 seconds
10561 el.fadeOut({ endOpacity: .25, duration: 2});
10562
10563 // common config options shown with default values
10564 el.fadeOut({
10565     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10566     easing: 'easeOut',
10567     duration: .5
10568     remove: false,
10569     useDisplay: false
10570 });
10571 </code></pre>
10572     * @param {Object} options (optional) Object literal with any of the Fx config options
10573     * @return {Roo.Element} The Element
10574     */
10575     fadeOut : function(o){
10576         var el = this.getFxEl();
10577         o = o || {};
10578         el.queueFx(o, function(){
10579             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10580                 o, null, .5, "easeOut", function(){
10581                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10582                      this.dom.style.display = "none";
10583                 }else{
10584                      this.dom.style.visibility = "hidden";
10585                 }
10586                 this.clearOpacity();
10587                 el.afterFx(o);
10588             });
10589         });
10590         return this;
10591     },
10592
10593    /**
10594     * Animates the transition of an element's dimensions from a starting height/width
10595     * to an ending height/width.
10596     * Usage:
10597 <pre><code>
10598 // change height and width to 100x100 pixels
10599 el.scale(100, 100);
10600
10601 // common config options shown with default values.  The height and width will default to
10602 // the element's existing values if passed as null.
10603 el.scale(
10604     [element's width],
10605     [element's height], {
10606     easing: 'easeOut',
10607     duration: .35
10608 });
10609 </code></pre>
10610     * @param {Number} width  The new width (pass undefined to keep the original width)
10611     * @param {Number} height  The new height (pass undefined to keep the original height)
10612     * @param {Object} options (optional) Object literal with any of the Fx config options
10613     * @return {Roo.Element} The Element
10614     */
10615     scale : function(w, h, o){
10616         this.shift(Roo.apply({}, o, {
10617             width: w,
10618             height: h
10619         }));
10620         return this;
10621     },
10622
10623    /**
10624     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10625     * Any of these properties not specified in the config object will not be changed.  This effect 
10626     * requires that at least one new dimension, position or opacity setting must be passed in on
10627     * the config object in order for the function to have any effect.
10628     * Usage:
10629 <pre><code>
10630 // slide the element horizontally to x position 200 while changing the height and opacity
10631 el.shift({ x: 200, height: 50, opacity: .8 });
10632
10633 // common config options shown with default values.
10634 el.shift({
10635     width: [element's width],
10636     height: [element's height],
10637     x: [element's x position],
10638     y: [element's y position],
10639     opacity: [element's opacity],
10640     easing: 'easeOut',
10641     duration: .35
10642 });
10643 </code></pre>
10644     * @param {Object} options  Object literal with any of the Fx config options
10645     * @return {Roo.Element} The Element
10646     */
10647     shift : function(o){
10648         var el = this.getFxEl();
10649         o = o || {};
10650         el.queueFx(o, function(){
10651             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10652             if(w !== undefined){
10653                 a.width = {to: this.adjustWidth(w)};
10654             }
10655             if(h !== undefined){
10656                 a.height = {to: this.adjustHeight(h)};
10657             }
10658             if(x !== undefined || y !== undefined){
10659                 a.points = {to: [
10660                     x !== undefined ? x : this.getX(),
10661                     y !== undefined ? y : this.getY()
10662                 ]};
10663             }
10664             if(op !== undefined){
10665                 a.opacity = {to: op};
10666             }
10667             if(o.xy !== undefined){
10668                 a.points = {to: o.xy};
10669             }
10670             arguments.callee.anim = this.fxanim(a,
10671                 o, 'motion', .35, "easeOut", function(){
10672                 el.afterFx(o);
10673             });
10674         });
10675         return this;
10676     },
10677
10678         /**
10679          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10680          * ending point of the effect.
10681          * Usage:
10682          *<pre><code>
10683 // default: slide the element downward while fading out
10684 el.ghost();
10685
10686 // custom: slide the element out to the right with a 2-second duration
10687 el.ghost('r', { duration: 2 });
10688
10689 // common config options shown with default values
10690 el.ghost('b', {
10691     easing: 'easeOut',
10692     duration: .5
10693     remove: false,
10694     useDisplay: false
10695 });
10696 </code></pre>
10697          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10698          * @param {Object} options (optional) Object literal with any of the Fx config options
10699          * @return {Roo.Element} The Element
10700          */
10701     ghost : function(anchor, o){
10702         var el = this.getFxEl();
10703         o = o || {};
10704
10705         el.queueFx(o, function(){
10706             anchor = anchor || "b";
10707
10708             // restore values after effect
10709             var r = this.getFxRestore();
10710             var w = this.getWidth(),
10711                 h = this.getHeight();
10712
10713             var st = this.dom.style;
10714
10715             var after = function(){
10716                 if(o.useDisplay){
10717                     el.setDisplayed(false);
10718                 }else{
10719                     el.hide();
10720                 }
10721
10722                 el.clearOpacity();
10723                 el.setPositioning(r.pos);
10724                 st.width = r.width;
10725                 st.height = r.height;
10726
10727                 el.afterFx(o);
10728             };
10729
10730             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10731             switch(anchor.toLowerCase()){
10732                 case "t":
10733                     pt.by = [0, -h];
10734                 break;
10735                 case "l":
10736                     pt.by = [-w, 0];
10737                 break;
10738                 case "r":
10739                     pt.by = [w, 0];
10740                 break;
10741                 case "b":
10742                     pt.by = [0, h];
10743                 break;
10744                 case "tl":
10745                     pt.by = [-w, -h];
10746                 break;
10747                 case "bl":
10748                     pt.by = [-w, h];
10749                 break;
10750                 case "br":
10751                     pt.by = [w, h];
10752                 break;
10753                 case "tr":
10754                     pt.by = [w, -h];
10755                 break;
10756             }
10757
10758             arguments.callee.anim = this.fxanim(a,
10759                 o,
10760                 'motion',
10761                 .5,
10762                 "easeOut", after);
10763         });
10764         return this;
10765     },
10766
10767         /**
10768          * Ensures that all effects queued after syncFx is called on the element are
10769          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10770          * @return {Roo.Element} The Element
10771          */
10772     syncFx : function(){
10773         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10774             block : false,
10775             concurrent : true,
10776             stopFx : false
10777         });
10778         return this;
10779     },
10780
10781         /**
10782          * Ensures that all effects queued after sequenceFx is called on the element are
10783          * run in sequence.  This is the opposite of {@link #syncFx}.
10784          * @return {Roo.Element} The Element
10785          */
10786     sequenceFx : function(){
10787         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10788             block : false,
10789             concurrent : false,
10790             stopFx : false
10791         });
10792         return this;
10793     },
10794
10795         /* @private */
10796     nextFx : function(){
10797         var ef = this.fxQueue[0];
10798         if(ef){
10799             ef.call(this);
10800         }
10801     },
10802
10803         /**
10804          * Returns true if the element has any effects actively running or queued, else returns false.
10805          * @return {Boolean} True if element has active effects, else false
10806          */
10807     hasActiveFx : function(){
10808         return this.fxQueue && this.fxQueue[0];
10809     },
10810
10811         /**
10812          * Stops any running effects and clears the element's internal effects queue if it contains
10813          * any additional effects that haven't started yet.
10814          * @return {Roo.Element} The Element
10815          */
10816     stopFx : function(){
10817         if(this.hasActiveFx()){
10818             var cur = this.fxQueue[0];
10819             if(cur && cur.anim && cur.anim.isAnimated()){
10820                 this.fxQueue = [cur]; // clear out others
10821                 cur.anim.stop(true);
10822             }
10823         }
10824         return this;
10825     },
10826
10827         /* @private */
10828     beforeFx : function(o){
10829         if(this.hasActiveFx() && !o.concurrent){
10830            if(o.stopFx){
10831                this.stopFx();
10832                return true;
10833            }
10834            return false;
10835         }
10836         return true;
10837     },
10838
10839         /**
10840          * Returns true if the element is currently blocking so that no other effect can be queued
10841          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10842          * used to ensure that an effect initiated by a user action runs to completion prior to the
10843          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10844          * @return {Boolean} True if blocking, else false
10845          */
10846     hasFxBlock : function(){
10847         var q = this.fxQueue;
10848         return q && q[0] && q[0].block;
10849     },
10850
10851         /* @private */
10852     queueFx : function(o, fn){
10853         if(!this.fxQueue){
10854             this.fxQueue = [];
10855         }
10856         if(!this.hasFxBlock()){
10857             Roo.applyIf(o, this.fxDefaults);
10858             if(!o.concurrent){
10859                 var run = this.beforeFx(o);
10860                 fn.block = o.block;
10861                 this.fxQueue.push(fn);
10862                 if(run){
10863                     this.nextFx();
10864                 }
10865             }else{
10866                 fn.call(this);
10867             }
10868         }
10869         return this;
10870     },
10871
10872         /* @private */
10873     fxWrap : function(pos, o, vis){
10874         var wrap;
10875         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10876             var wrapXY;
10877             if(o.fixPosition){
10878                 wrapXY = this.getXY();
10879             }
10880             var div = document.createElement("div");
10881             div.style.visibility = vis;
10882             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10883             wrap.setPositioning(pos);
10884             if(wrap.getStyle("position") == "static"){
10885                 wrap.position("relative");
10886             }
10887             this.clearPositioning('auto');
10888             wrap.clip();
10889             wrap.dom.appendChild(this.dom);
10890             if(wrapXY){
10891                 wrap.setXY(wrapXY);
10892             }
10893         }
10894         return wrap;
10895     },
10896
10897         /* @private */
10898     fxUnwrap : function(wrap, pos, o){
10899         this.clearPositioning();
10900         this.setPositioning(pos);
10901         if(!o.wrap){
10902             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10903             wrap.remove();
10904         }
10905     },
10906
10907         /* @private */
10908     getFxRestore : function(){
10909         var st = this.dom.style;
10910         return {pos: this.getPositioning(), width: st.width, height : st.height};
10911     },
10912
10913         /* @private */
10914     afterFx : function(o){
10915         if(o.afterStyle){
10916             this.applyStyles(o.afterStyle);
10917         }
10918         if(o.afterCls){
10919             this.addClass(o.afterCls);
10920         }
10921         if(o.remove === true){
10922             this.remove();
10923         }
10924         Roo.callback(o.callback, o.scope, [this]);
10925         if(!o.concurrent){
10926             this.fxQueue.shift();
10927             this.nextFx();
10928         }
10929     },
10930
10931         /* @private */
10932     getFxEl : function(){ // support for composite element fx
10933         return Roo.get(this.dom);
10934     },
10935
10936         /* @private */
10937     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10938         animType = animType || 'run';
10939         opt = opt || {};
10940         var anim = Roo.lib.Anim[animType](
10941             this.dom, args,
10942             (opt.duration || defaultDur) || .35,
10943             (opt.easing || defaultEase) || 'easeOut',
10944             function(){
10945                 Roo.callback(cb, this);
10946             },
10947             this
10948         );
10949         opt.anim = anim;
10950         return anim;
10951     }
10952 };
10953
10954 // backwords compat
10955 Roo.Fx.resize = Roo.Fx.scale;
10956
10957 //When included, Roo.Fx is automatically applied to Element so that all basic
10958 //effects are available directly via the Element API
10959 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10960  * Based on:
10961  * Ext JS Library 1.1.1
10962  * Copyright(c) 2006-2007, Ext JS, LLC.
10963  *
10964  * Originally Released Under LGPL - original licence link has changed is not relivant.
10965  *
10966  * Fork - LGPL
10967  * <script type="text/javascript">
10968  */
10969
10970
10971 /**
10972  * @class Roo.CompositeElement
10973  * Standard composite class. Creates a Roo.Element for every element in the collection.
10974  * <br><br>
10975  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10976  * actions will be performed on all the elements in this collection.</b>
10977  * <br><br>
10978  * All methods return <i>this</i> and can be chained.
10979  <pre><code>
10980  var els = Roo.select("#some-el div.some-class", true);
10981  // or select directly from an existing element
10982  var el = Roo.get('some-el');
10983  el.select('div.some-class', true);
10984
10985  els.setWidth(100); // all elements become 100 width
10986  els.hide(true); // all elements fade out and hide
10987  // or
10988  els.setWidth(100).hide(true);
10989  </code></pre>
10990  */
10991 Roo.CompositeElement = function(els){
10992     this.elements = [];
10993     this.addElements(els);
10994 };
10995 Roo.CompositeElement.prototype = {
10996     isComposite: true,
10997     addElements : function(els){
10998         if(!els) return this;
10999         if(typeof els == "string"){
11000             els = Roo.Element.selectorFunction(els);
11001         }
11002         var yels = this.elements;
11003         var index = yels.length-1;
11004         for(var i = 0, len = els.length; i < len; i++) {
11005                 yels[++index] = Roo.get(els[i]);
11006         }
11007         return this;
11008     },
11009
11010     /**
11011     * Clears this composite and adds the elements returned by the passed selector.
11012     * @param {String/Array} els A string CSS selector, an array of elements or an element
11013     * @return {CompositeElement} this
11014     */
11015     fill : function(els){
11016         this.elements = [];
11017         this.add(els);
11018         return this;
11019     },
11020
11021     /**
11022     * Filters this composite to only elements that match the passed selector.
11023     * @param {String} selector A string CSS selector
11024     * @param {Boolean} inverse return inverse filter (not matches)
11025     * @return {CompositeElement} this
11026     */
11027     filter : function(selector, inverse){
11028         var els = [];
11029         inverse = inverse || false;
11030         this.each(function(el){
11031             var match = inverse ? !el.is(selector) : el.is(selector);
11032             if(match){
11033                 els[els.length] = el.dom;
11034             }
11035         });
11036         this.fill(els);
11037         return this;
11038     },
11039
11040     invoke : function(fn, args){
11041         var els = this.elements;
11042         for(var i = 0, len = els.length; i < len; i++) {
11043                 Roo.Element.prototype[fn].apply(els[i], args);
11044         }
11045         return this;
11046     },
11047     /**
11048     * Adds elements to this composite.
11049     * @param {String/Array} els A string CSS selector, an array of elements or an element
11050     * @return {CompositeElement} this
11051     */
11052     add : function(els){
11053         if(typeof els == "string"){
11054             this.addElements(Roo.Element.selectorFunction(els));
11055         }else if(els.length !== undefined){
11056             this.addElements(els);
11057         }else{
11058             this.addElements([els]);
11059         }
11060         return this;
11061     },
11062     /**
11063     * Calls the passed function passing (el, this, index) for each element in this composite.
11064     * @param {Function} fn The function to call
11065     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11066     * @return {CompositeElement} this
11067     */
11068     each : function(fn, scope){
11069         var els = this.elements;
11070         for(var i = 0, len = els.length; i < len; i++){
11071             if(fn.call(scope || els[i], els[i], this, i) === false) {
11072                 break;
11073             }
11074         }
11075         return this;
11076     },
11077
11078     /**
11079      * Returns the Element object at the specified index
11080      * @param {Number} index
11081      * @return {Roo.Element}
11082      */
11083     item : function(index){
11084         return this.elements[index] || null;
11085     },
11086
11087     /**
11088      * Returns the first Element
11089      * @return {Roo.Element}
11090      */
11091     first : function(){
11092         return this.item(0);
11093     },
11094
11095     /**
11096      * Returns the last Element
11097      * @return {Roo.Element}
11098      */
11099     last : function(){
11100         return this.item(this.elements.length-1);
11101     },
11102
11103     /**
11104      * Returns the number of elements in this composite
11105      * @return Number
11106      */
11107     getCount : function(){
11108         return this.elements.length;
11109     },
11110
11111     /**
11112      * Returns true if this composite contains the passed element
11113      * @return Boolean
11114      */
11115     contains : function(el){
11116         return this.indexOf(el) !== -1;
11117     },
11118
11119     /**
11120      * Returns true if this composite contains the passed element
11121      * @return Boolean
11122      */
11123     indexOf : function(el){
11124         return this.elements.indexOf(Roo.get(el));
11125     },
11126
11127
11128     /**
11129     * Removes the specified element(s).
11130     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11131     * or an array of any of those.
11132     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11133     * @return {CompositeElement} this
11134     */
11135     removeElement : function(el, removeDom){
11136         if(el instanceof Array){
11137             for(var i = 0, len = el.length; i < len; i++){
11138                 this.removeElement(el[i]);
11139             }
11140             return this;
11141         }
11142         var index = typeof el == 'number' ? el : this.indexOf(el);
11143         if(index !== -1){
11144             if(removeDom){
11145                 var d = this.elements[index];
11146                 if(d.dom){
11147                     d.remove();
11148                 }else{
11149                     d.parentNode.removeChild(d);
11150                 }
11151             }
11152             this.elements.splice(index, 1);
11153         }
11154         return this;
11155     },
11156
11157     /**
11158     * Replaces the specified element with the passed element.
11159     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11160     * to replace.
11161     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11162     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11163     * @return {CompositeElement} this
11164     */
11165     replaceElement : function(el, replacement, domReplace){
11166         var index = typeof el == 'number' ? el : this.indexOf(el);
11167         if(index !== -1){
11168             if(domReplace){
11169                 this.elements[index].replaceWith(replacement);
11170             }else{
11171                 this.elements.splice(index, 1, Roo.get(replacement))
11172             }
11173         }
11174         return this;
11175     },
11176
11177     /**
11178      * Removes all elements.
11179      */
11180     clear : function(){
11181         this.elements = [];
11182     }
11183 };
11184 (function(){
11185     Roo.CompositeElement.createCall = function(proto, fnName){
11186         if(!proto[fnName]){
11187             proto[fnName] = function(){
11188                 return this.invoke(fnName, arguments);
11189             };
11190         }
11191     };
11192     for(var fnName in Roo.Element.prototype){
11193         if(typeof Roo.Element.prototype[fnName] == "function"){
11194             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11195         }
11196     };
11197 })();
11198 /*
11199  * Based on:
11200  * Ext JS Library 1.1.1
11201  * Copyright(c) 2006-2007, Ext JS, LLC.
11202  *
11203  * Originally Released Under LGPL - original licence link has changed is not relivant.
11204  *
11205  * Fork - LGPL
11206  * <script type="text/javascript">
11207  */
11208
11209 /**
11210  * @class Roo.CompositeElementLite
11211  * @extends Roo.CompositeElement
11212  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11213  <pre><code>
11214  var els = Roo.select("#some-el div.some-class");
11215  // or select directly from an existing element
11216  var el = Roo.get('some-el');
11217  el.select('div.some-class');
11218
11219  els.setWidth(100); // all elements become 100 width
11220  els.hide(true); // all elements fade out and hide
11221  // or
11222  els.setWidth(100).hide(true);
11223  </code></pre><br><br>
11224  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11225  * actions will be performed on all the elements in this collection.</b>
11226  */
11227 Roo.CompositeElementLite = function(els){
11228     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11229     this.el = new Roo.Element.Flyweight();
11230 };
11231 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11232     addElements : function(els){
11233         if(els){
11234             if(els instanceof Array){
11235                 this.elements = this.elements.concat(els);
11236             }else{
11237                 var yels = this.elements;
11238                 var index = yels.length-1;
11239                 for(var i = 0, len = els.length; i < len; i++) {
11240                     yels[++index] = els[i];
11241                 }
11242             }
11243         }
11244         return this;
11245     },
11246     invoke : function(fn, args){
11247         var els = this.elements;
11248         var el = this.el;
11249         for(var i = 0, len = els.length; i < len; i++) {
11250             el.dom = els[i];
11251                 Roo.Element.prototype[fn].apply(el, args);
11252         }
11253         return this;
11254     },
11255     /**
11256      * Returns a flyweight Element of the dom element object at the specified index
11257      * @param {Number} index
11258      * @return {Roo.Element}
11259      */
11260     item : function(index){
11261         if(!this.elements[index]){
11262             return null;
11263         }
11264         this.el.dom = this.elements[index];
11265         return this.el;
11266     },
11267
11268     // fixes scope with flyweight
11269     addListener : function(eventName, handler, scope, opt){
11270         var els = this.elements;
11271         for(var i = 0, len = els.length; i < len; i++) {
11272             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11273         }
11274         return this;
11275     },
11276
11277     /**
11278     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11279     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11280     * a reference to the dom node, use el.dom.</b>
11281     * @param {Function} fn The function to call
11282     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11283     * @return {CompositeElement} this
11284     */
11285     each : function(fn, scope){
11286         var els = this.elements;
11287         var el = this.el;
11288         for(var i = 0, len = els.length; i < len; i++){
11289             el.dom = els[i];
11290                 if(fn.call(scope || el, el, this, i) === false){
11291                 break;
11292             }
11293         }
11294         return this;
11295     },
11296
11297     indexOf : function(el){
11298         return this.elements.indexOf(Roo.getDom(el));
11299     },
11300
11301     replaceElement : function(el, replacement, domReplace){
11302         var index = typeof el == 'number' ? el : this.indexOf(el);
11303         if(index !== -1){
11304             replacement = Roo.getDom(replacement);
11305             if(domReplace){
11306                 var d = this.elements[index];
11307                 d.parentNode.insertBefore(replacement, d);
11308                 d.parentNode.removeChild(d);
11309             }
11310             this.elements.splice(index, 1, replacement);
11311         }
11312         return this;
11313     }
11314 });
11315 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11316
11317 /*
11318  * Based on:
11319  * Ext JS Library 1.1.1
11320  * Copyright(c) 2006-2007, Ext JS, LLC.
11321  *
11322  * Originally Released Under LGPL - original licence link has changed is not relivant.
11323  *
11324  * Fork - LGPL
11325  * <script type="text/javascript">
11326  */
11327
11328  
11329
11330 /**
11331  * @class Roo.data.Connection
11332  * @extends Roo.util.Observable
11333  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11334  * either to a configured URL, or to a URL specified at request time.<br><br>
11335  * <p>
11336  * Requests made by this class are asynchronous, and will return immediately. No data from
11337  * the server will be available to the statement immediately following the {@link #request} call.
11338  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11339  * <p>
11340  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11341  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11342  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11343  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11344  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11345  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11346  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11347  * standard DOM methods.
11348  * @constructor
11349  * @param {Object} config a configuration object.
11350  */
11351 Roo.data.Connection = function(config){
11352     Roo.apply(this, config);
11353     this.addEvents({
11354         /**
11355          * @event beforerequest
11356          * Fires before a network request is made to retrieve a data object.
11357          * @param {Connection} conn This Connection object.
11358          * @param {Object} options The options config object passed to the {@link #request} method.
11359          */
11360         "beforerequest" : true,
11361         /**
11362          * @event requestcomplete
11363          * Fires if the request was successfully completed.
11364          * @param {Connection} conn This Connection object.
11365          * @param {Object} response The XHR object containing the response data.
11366          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11367          * @param {Object} options The options config object passed to the {@link #request} method.
11368          */
11369         "requestcomplete" : true,
11370         /**
11371          * @event requestexception
11372          * Fires if an error HTTP status was returned from the server.
11373          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11374          * @param {Connection} conn This Connection object.
11375          * @param {Object} response The XHR object containing the response data.
11376          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11377          * @param {Object} options The options config object passed to the {@link #request} method.
11378          */
11379         "requestexception" : true
11380     });
11381     Roo.data.Connection.superclass.constructor.call(this);
11382 };
11383
11384 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11385     /**
11386      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11387      */
11388     /**
11389      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11390      * extra parameters to each request made by this object. (defaults to undefined)
11391      */
11392     /**
11393      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11394      *  to each request made by this object. (defaults to undefined)
11395      */
11396     /**
11397      * @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)
11398      */
11399     /**
11400      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11401      */
11402     timeout : 30000,
11403     /**
11404      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11405      * @type Boolean
11406      */
11407     autoAbort:false,
11408
11409     /**
11410      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11411      * @type Boolean
11412      */
11413     disableCaching: true,
11414
11415     /**
11416      * Sends an HTTP request to a remote server.
11417      * @param {Object} options An object which may contain the following properties:<ul>
11418      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11419      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11420      * request, a url encoded string or a function to call to get either.</li>
11421      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11422      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11423      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11424      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11425      * <li>options {Object} The parameter to the request call.</li>
11426      * <li>success {Boolean} True if the request succeeded.</li>
11427      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11428      * </ul></li>
11429      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11430      * The callback is passed the following parameters:<ul>
11431      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11432      * <li>options {Object} The parameter to the request call.</li>
11433      * </ul></li>
11434      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11435      * The callback is passed the following parameters:<ul>
11436      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11437      * <li>options {Object} The parameter to the request call.</li>
11438      * </ul></li>
11439      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11440      * for the callback function. Defaults to the browser window.</li>
11441      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11442      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11443      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11444      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11445      * params for the post data. Any params will be appended to the URL.</li>
11446      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11447      * </ul>
11448      * @return {Number} transactionId
11449      */
11450     request : function(o){
11451         if(this.fireEvent("beforerequest", this, o) !== false){
11452             var p = o.params;
11453
11454             if(typeof p == "function"){
11455                 p = p.call(o.scope||window, o);
11456             }
11457             if(typeof p == "object"){
11458                 p = Roo.urlEncode(o.params);
11459             }
11460             if(this.extraParams){
11461                 var extras = Roo.urlEncode(this.extraParams);
11462                 p = p ? (p + '&' + extras) : extras;
11463             }
11464
11465             var url = o.url || this.url;
11466             if(typeof url == 'function'){
11467                 url = url.call(o.scope||window, o);
11468             }
11469
11470             if(o.form){
11471                 var form = Roo.getDom(o.form);
11472                 url = url || form.action;
11473
11474                 var enctype = form.getAttribute("enctype");
11475                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11476                     return this.doFormUpload(o, p, url);
11477                 }
11478                 var f = Roo.lib.Ajax.serializeForm(form);
11479                 p = p ? (p + '&' + f) : f;
11480             }
11481
11482             var hs = o.headers;
11483             if(this.defaultHeaders){
11484                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11485                 if(!o.headers){
11486                     o.headers = hs;
11487                 }
11488             }
11489
11490             var cb = {
11491                 success: this.handleResponse,
11492                 failure: this.handleFailure,
11493                 scope: this,
11494                 argument: {options: o},
11495                 timeout : o.timeout || this.timeout
11496             };
11497
11498             var method = o.method||this.method||(p ? "POST" : "GET");
11499
11500             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11501                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11502             }
11503
11504             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11505                 if(o.autoAbort){
11506                     this.abort();
11507                 }
11508             }else if(this.autoAbort !== false){
11509                 this.abort();
11510             }
11511
11512             if((method == 'GET' && p) || o.xmlData){
11513                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11514                 p = '';
11515             }
11516             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11517             return this.transId;
11518         }else{
11519             Roo.callback(o.callback, o.scope, [o, null, null]);
11520             return null;
11521         }
11522     },
11523
11524     /**
11525      * Determine whether this object has a request outstanding.
11526      * @param {Number} transactionId (Optional) defaults to the last transaction
11527      * @return {Boolean} True if there is an outstanding request.
11528      */
11529     isLoading : function(transId){
11530         if(transId){
11531             return Roo.lib.Ajax.isCallInProgress(transId);
11532         }else{
11533             return this.transId ? true : false;
11534         }
11535     },
11536
11537     /**
11538      * Aborts any outstanding request.
11539      * @param {Number} transactionId (Optional) defaults to the last transaction
11540      */
11541     abort : function(transId){
11542         if(transId || this.isLoading()){
11543             Roo.lib.Ajax.abort(transId || this.transId);
11544         }
11545     },
11546
11547     // private
11548     handleResponse : function(response){
11549         this.transId = false;
11550         var options = response.argument.options;
11551         response.argument = options ? options.argument : null;
11552         this.fireEvent("requestcomplete", this, response, options);
11553         Roo.callback(options.success, options.scope, [response, options]);
11554         Roo.callback(options.callback, options.scope, [options, true, response]);
11555     },
11556
11557     // private
11558     handleFailure : function(response, e){
11559         this.transId = false;
11560         var options = response.argument.options;
11561         response.argument = options ? options.argument : null;
11562         this.fireEvent("requestexception", this, response, options, e);
11563         Roo.callback(options.failure, options.scope, [response, options]);
11564         Roo.callback(options.callback, options.scope, [options, false, response]);
11565     },
11566
11567     // private
11568     doFormUpload : function(o, ps, url){
11569         var id = Roo.id();
11570         var frame = document.createElement('iframe');
11571         frame.id = id;
11572         frame.name = id;
11573         frame.className = 'x-hidden';
11574         if(Roo.isIE){
11575             frame.src = Roo.SSL_SECURE_URL;
11576         }
11577         document.body.appendChild(frame);
11578
11579         if(Roo.isIE){
11580            document.frames[id].name = id;
11581         }
11582
11583         var form = Roo.getDom(o.form);
11584         form.target = id;
11585         form.method = 'POST';
11586         form.enctype = form.encoding = 'multipart/form-data';
11587         if(url){
11588             form.action = url;
11589         }
11590
11591         var hiddens, hd;
11592         if(ps){ // add dynamic params
11593             hiddens = [];
11594             ps = Roo.urlDecode(ps, false);
11595             for(var k in ps){
11596                 if(ps.hasOwnProperty(k)){
11597                     hd = document.createElement('input');
11598                     hd.type = 'hidden';
11599                     hd.name = k;
11600                     hd.value = ps[k];
11601                     form.appendChild(hd);
11602                     hiddens.push(hd);
11603                 }
11604             }
11605         }
11606
11607         function cb(){
11608             var r = {  // bogus response object
11609                 responseText : '',
11610                 responseXML : null
11611             };
11612
11613             r.argument = o ? o.argument : null;
11614
11615             try { //
11616                 var doc;
11617                 if(Roo.isIE){
11618                     doc = frame.contentWindow.document;
11619                 }else {
11620                     doc = (frame.contentDocument || window.frames[id].document);
11621                 }
11622                 if(doc && doc.body){
11623                     r.responseText = doc.body.innerHTML;
11624                 }
11625                 if(doc && doc.XMLDocument){
11626                     r.responseXML = doc.XMLDocument;
11627                 }else {
11628                     r.responseXML = doc;
11629                 }
11630             }
11631             catch(e) {
11632                 // ignore
11633             }
11634
11635             Roo.EventManager.removeListener(frame, 'load', cb, this);
11636
11637             this.fireEvent("requestcomplete", this, r, o);
11638             Roo.callback(o.success, o.scope, [r, o]);
11639             Roo.callback(o.callback, o.scope, [o, true, r]);
11640
11641             setTimeout(function(){document.body.removeChild(frame);}, 100);
11642         }
11643
11644         Roo.EventManager.on(frame, 'load', cb, this);
11645         form.submit();
11646
11647         if(hiddens){ // remove dynamic params
11648             for(var i = 0, len = hiddens.length; i < len; i++){
11649                 form.removeChild(hiddens[i]);
11650             }
11651         }
11652     }
11653 });
11654 /*
11655  * Based on:
11656  * Ext JS Library 1.1.1
11657  * Copyright(c) 2006-2007, Ext JS, LLC.
11658  *
11659  * Originally Released Under LGPL - original licence link has changed is not relivant.
11660  *
11661  * Fork - LGPL
11662  * <script type="text/javascript">
11663  */
11664  
11665 /**
11666  * Global Ajax request class.
11667  * 
11668  * @class Roo.Ajax
11669  * @extends Roo.data.Connection
11670  * @static
11671  * 
11672  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11673  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11674  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11675  * @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)
11676  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11677  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11678  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11679  */
11680 Roo.Ajax = new Roo.data.Connection({
11681     // fix up the docs
11682     /**
11683      * @scope Roo.Ajax
11684      * @type {Boolear} 
11685      */
11686     autoAbort : false,
11687
11688     /**
11689      * Serialize the passed form into a url encoded string
11690      * @scope Roo.Ajax
11691      * @param {String/HTMLElement} form
11692      * @return {String}
11693      */
11694     serializeForm : function(form){
11695         return Roo.lib.Ajax.serializeForm(form);
11696     }
11697 });/*
11698  * Based on:
11699  * Ext JS Library 1.1.1
11700  * Copyright(c) 2006-2007, Ext JS, LLC.
11701  *
11702  * Originally Released Under LGPL - original licence link has changed is not relivant.
11703  *
11704  * Fork - LGPL
11705  * <script type="text/javascript">
11706  */
11707
11708  
11709 /**
11710  * @class Roo.UpdateManager
11711  * @extends Roo.util.Observable
11712  * Provides AJAX-style update for Element object.<br><br>
11713  * Usage:<br>
11714  * <pre><code>
11715  * // Get it from a Roo.Element object
11716  * var el = Roo.get("foo");
11717  * var mgr = el.getUpdateManager();
11718  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11719  * ...
11720  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11721  * <br>
11722  * // or directly (returns the same UpdateManager instance)
11723  * var mgr = new Roo.UpdateManager("myElementId");
11724  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11725  * mgr.on("update", myFcnNeedsToKnow);
11726  * <br>
11727    // short handed call directly from the element object
11728    Roo.get("foo").load({
11729         url: "bar.php",
11730         scripts:true,
11731         params: "for=bar",
11732         text: "Loading Foo..."
11733    });
11734  * </code></pre>
11735  * @constructor
11736  * Create new UpdateManager directly.
11737  * @param {String/HTMLElement/Roo.Element} el The element to update
11738  * @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).
11739  */
11740 Roo.UpdateManager = function(el, forceNew){
11741     el = Roo.get(el);
11742     if(!forceNew && el.updateManager){
11743         return el.updateManager;
11744     }
11745     /**
11746      * The Element object
11747      * @type Roo.Element
11748      */
11749     this.el = el;
11750     /**
11751      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11752      * @type String
11753      */
11754     this.defaultUrl = null;
11755
11756     this.addEvents({
11757         /**
11758          * @event beforeupdate
11759          * Fired before an update is made, return false from your handler and the update is cancelled.
11760          * @param {Roo.Element} el
11761          * @param {String/Object/Function} url
11762          * @param {String/Object} params
11763          */
11764         "beforeupdate": true,
11765         /**
11766          * @event update
11767          * Fired after successful update is made.
11768          * @param {Roo.Element} el
11769          * @param {Object} oResponseObject The response Object
11770          */
11771         "update": true,
11772         /**
11773          * @event failure
11774          * Fired on update failure.
11775          * @param {Roo.Element} el
11776          * @param {Object} oResponseObject The response Object
11777          */
11778         "failure": true
11779     });
11780     var d = Roo.UpdateManager.defaults;
11781     /**
11782      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11783      * @type String
11784      */
11785     this.sslBlankUrl = d.sslBlankUrl;
11786     /**
11787      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11788      * @type Boolean
11789      */
11790     this.disableCaching = d.disableCaching;
11791     /**
11792      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11793      * @type String
11794      */
11795     this.indicatorText = d.indicatorText;
11796     /**
11797      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11798      * @type String
11799      */
11800     this.showLoadIndicator = d.showLoadIndicator;
11801     /**
11802      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11803      * @type Number
11804      */
11805     this.timeout = d.timeout;
11806
11807     /**
11808      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11809      * @type Boolean
11810      */
11811     this.loadScripts = d.loadScripts;
11812
11813     /**
11814      * Transaction object of current executing transaction
11815      */
11816     this.transaction = null;
11817
11818     /**
11819      * @private
11820      */
11821     this.autoRefreshProcId = null;
11822     /**
11823      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11824      * @type Function
11825      */
11826     this.refreshDelegate = this.refresh.createDelegate(this);
11827     /**
11828      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11829      * @type Function
11830      */
11831     this.updateDelegate = this.update.createDelegate(this);
11832     /**
11833      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11834      * @type Function
11835      */
11836     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11837     /**
11838      * @private
11839      */
11840     this.successDelegate = this.processSuccess.createDelegate(this);
11841     /**
11842      * @private
11843      */
11844     this.failureDelegate = this.processFailure.createDelegate(this);
11845
11846     if(!this.renderer){
11847      /**
11848       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11849       */
11850     this.renderer = new Roo.UpdateManager.BasicRenderer();
11851     }
11852     
11853     Roo.UpdateManager.superclass.constructor.call(this);
11854 };
11855
11856 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11857     /**
11858      * Get the Element this UpdateManager is bound to
11859      * @return {Roo.Element} The element
11860      */
11861     getEl : function(){
11862         return this.el;
11863     },
11864     /**
11865      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11866      * @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:
11867 <pre><code>
11868 um.update({<br/>
11869     url: "your-url.php",<br/>
11870     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11871     callback: yourFunction,<br/>
11872     scope: yourObject, //(optional scope)  <br/>
11873     discardUrl: false, <br/>
11874     nocache: false,<br/>
11875     text: "Loading...",<br/>
11876     timeout: 30,<br/>
11877     scripts: false<br/>
11878 });
11879 </code></pre>
11880      * The only required property is url. The optional properties nocache, text and scripts
11881      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11882      * @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}
11883      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11884      * @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.
11885      */
11886     update : function(url, params, callback, discardUrl){
11887         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11888             var method = this.method,
11889                 cfg;
11890             if(typeof url == "object"){ // must be config object
11891                 cfg = url;
11892                 url = cfg.url;
11893                 params = params || cfg.params;
11894                 callback = callback || cfg.callback;
11895                 discardUrl = discardUrl || cfg.discardUrl;
11896                 if(callback && cfg.scope){
11897                     callback = callback.createDelegate(cfg.scope);
11898                 }
11899                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11900                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11901                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11902                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11903                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11904             }
11905             this.showLoading();
11906             if(!discardUrl){
11907                 this.defaultUrl = url;
11908             }
11909             if(typeof url == "function"){
11910                 url = url.call(this);
11911             }
11912
11913             method = method || (params ? "POST" : "GET");
11914             if(method == "GET"){
11915                 url = this.prepareUrl(url);
11916             }
11917
11918             var o = Roo.apply(cfg ||{}, {
11919                 url : url,
11920                 params: params,
11921                 success: this.successDelegate,
11922                 failure: this.failureDelegate,
11923                 callback: undefined,
11924                 timeout: (this.timeout*1000),
11925                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11926             });
11927             Roo.log("updated manager called with timeout of " + o.timeout);
11928             this.transaction = Roo.Ajax.request(o);
11929         }
11930     },
11931
11932     /**
11933      * 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.
11934      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11935      * @param {String/HTMLElement} form The form Id or form element
11936      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11937      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11938      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11939      */
11940     formUpdate : function(form, url, reset, callback){
11941         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11942             if(typeof url == "function"){
11943                 url = url.call(this);
11944             }
11945             form = Roo.getDom(form);
11946             this.transaction = Roo.Ajax.request({
11947                 form: form,
11948                 url:url,
11949                 success: this.successDelegate,
11950                 failure: this.failureDelegate,
11951                 timeout: (this.timeout*1000),
11952                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11953             });
11954             this.showLoading.defer(1, this);
11955         }
11956     },
11957
11958     /**
11959      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11960      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11961      */
11962     refresh : function(callback){
11963         if(this.defaultUrl == null){
11964             return;
11965         }
11966         this.update(this.defaultUrl, null, callback, true);
11967     },
11968
11969     /**
11970      * Set this element to auto refresh.
11971      * @param {Number} interval How often to update (in seconds).
11972      * @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)
11973      * @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}
11974      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11975      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11976      */
11977     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11978         if(refreshNow){
11979             this.update(url || this.defaultUrl, params, callback, true);
11980         }
11981         if(this.autoRefreshProcId){
11982             clearInterval(this.autoRefreshProcId);
11983         }
11984         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11985     },
11986
11987     /**
11988      * Stop auto refresh on this element.
11989      */
11990      stopAutoRefresh : function(){
11991         if(this.autoRefreshProcId){
11992             clearInterval(this.autoRefreshProcId);
11993             delete this.autoRefreshProcId;
11994         }
11995     },
11996
11997     isAutoRefreshing : function(){
11998        return this.autoRefreshProcId ? true : false;
11999     },
12000     /**
12001      * Called to update the element to "Loading" state. Override to perform custom action.
12002      */
12003     showLoading : function(){
12004         if(this.showLoadIndicator){
12005             this.el.update(this.indicatorText);
12006         }
12007     },
12008
12009     /**
12010      * Adds unique parameter to query string if disableCaching = true
12011      * @private
12012      */
12013     prepareUrl : function(url){
12014         if(this.disableCaching){
12015             var append = "_dc=" + (new Date().getTime());
12016             if(url.indexOf("?") !== -1){
12017                 url += "&" + append;
12018             }else{
12019                 url += "?" + append;
12020             }
12021         }
12022         return url;
12023     },
12024
12025     /**
12026      * @private
12027      */
12028     processSuccess : function(response){
12029         this.transaction = null;
12030         if(response.argument.form && response.argument.reset){
12031             try{ // put in try/catch since some older FF releases had problems with this
12032                 response.argument.form.reset();
12033             }catch(e){}
12034         }
12035         if(this.loadScripts){
12036             this.renderer.render(this.el, response, this,
12037                 this.updateComplete.createDelegate(this, [response]));
12038         }else{
12039             this.renderer.render(this.el, response, this);
12040             this.updateComplete(response);
12041         }
12042     },
12043
12044     updateComplete : function(response){
12045         this.fireEvent("update", this.el, response);
12046         if(typeof response.argument.callback == "function"){
12047             response.argument.callback(this.el, true, response);
12048         }
12049     },
12050
12051     /**
12052      * @private
12053      */
12054     processFailure : function(response){
12055         this.transaction = null;
12056         this.fireEvent("failure", this.el, response);
12057         if(typeof response.argument.callback == "function"){
12058             response.argument.callback(this.el, false, response);
12059         }
12060     },
12061
12062     /**
12063      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12064      * @param {Object} renderer The object implementing the render() method
12065      */
12066     setRenderer : function(renderer){
12067         this.renderer = renderer;
12068     },
12069
12070     getRenderer : function(){
12071        return this.renderer;
12072     },
12073
12074     /**
12075      * Set the defaultUrl used for updates
12076      * @param {String/Function} defaultUrl The url or a function to call to get the url
12077      */
12078     setDefaultUrl : function(defaultUrl){
12079         this.defaultUrl = defaultUrl;
12080     },
12081
12082     /**
12083      * Aborts the executing transaction
12084      */
12085     abort : function(){
12086         if(this.transaction){
12087             Roo.Ajax.abort(this.transaction);
12088         }
12089     },
12090
12091     /**
12092      * Returns true if an update is in progress
12093      * @return {Boolean}
12094      */
12095     isUpdating : function(){
12096         if(this.transaction){
12097             return Roo.Ajax.isLoading(this.transaction);
12098         }
12099         return false;
12100     }
12101 });
12102
12103 /**
12104  * @class Roo.UpdateManager.defaults
12105  * @static (not really - but it helps the doc tool)
12106  * The defaults collection enables customizing the default properties of UpdateManager
12107  */
12108    Roo.UpdateManager.defaults = {
12109        /**
12110          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12111          * @type Number
12112          */
12113          timeout : 30,
12114
12115          /**
12116          * True to process scripts by default (Defaults to false).
12117          * @type Boolean
12118          */
12119         loadScripts : false,
12120
12121         /**
12122         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12123         * @type String
12124         */
12125         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12126         /**
12127          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12128          * @type Boolean
12129          */
12130         disableCaching : false,
12131         /**
12132          * Whether to show indicatorText when loading (Defaults to true).
12133          * @type Boolean
12134          */
12135         showLoadIndicator : true,
12136         /**
12137          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12138          * @type String
12139          */
12140         indicatorText : '<div class="loading-indicator">Loading...</div>'
12141    };
12142
12143 /**
12144  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12145  *Usage:
12146  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12147  * @param {String/HTMLElement/Roo.Element} el The element to update
12148  * @param {String} url The url
12149  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12150  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12151  * @static
12152  * @deprecated
12153  * @member Roo.UpdateManager
12154  */
12155 Roo.UpdateManager.updateElement = function(el, url, params, options){
12156     var um = Roo.get(el, true).getUpdateManager();
12157     Roo.apply(um, options);
12158     um.update(url, params, options ? options.callback : null);
12159 };
12160 // alias for backwards compat
12161 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12162 /**
12163  * @class Roo.UpdateManager.BasicRenderer
12164  * Default Content renderer. Updates the elements innerHTML with the responseText.
12165  */
12166 Roo.UpdateManager.BasicRenderer = function(){};
12167
12168 Roo.UpdateManager.BasicRenderer.prototype = {
12169     /**
12170      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12171      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12172      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12173      * @param {Roo.Element} el The element being rendered
12174      * @param {Object} response The YUI Connect response object
12175      * @param {UpdateManager} updateManager The calling update manager
12176      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12177      */
12178      render : function(el, response, updateManager, callback){
12179         el.update(response.responseText, updateManager.loadScripts, callback);
12180     }
12181 };
12182 /*
12183  * Based on:
12184  * Roo JS
12185  * (c)) Alan Knowles
12186  * Licence : LGPL
12187  */
12188
12189
12190 /**
12191  * @class Roo.DomTemplate
12192  * @extends Roo.Template
12193  * An effort at a dom based template engine..
12194  *
12195  * Similar to XTemplate, except it uses dom parsing to create the template..
12196  *
12197  * Supported features:
12198  *
12199  *  Tags:
12200
12201 <pre><code>
12202       {a_variable} - output encoded.
12203       {a_variable.format:("Y-m-d")} - call a method on the variable
12204       {a_variable:raw} - unencoded output
12205       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12206       {a_variable:this.method_on_template(...)} - call a method on the template object.
12207  
12208 </code></pre>
12209  *  The tpl tag:
12210 <pre><code>
12211         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12212         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12213         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12214         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12215   
12216 </code></pre>
12217  *      
12218  */
12219 Roo.DomTemplate = function()
12220 {
12221      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12222      if (this.html) {
12223         this.compile();
12224      }
12225 };
12226
12227
12228 Roo.extend(Roo.DomTemplate, Roo.Template, {
12229     /**
12230      * id counter for sub templates.
12231      */
12232     id : 0,
12233     /**
12234      * flag to indicate if dom parser is inside a pre,
12235      * it will strip whitespace if not.
12236      */
12237     inPre : false,
12238     
12239     /**
12240      * The various sub templates
12241      */
12242     tpls : false,
12243     
12244     
12245     
12246     /**
12247      *
12248      * basic tag replacing syntax
12249      * WORD:WORD()
12250      *
12251      * // you can fake an object call by doing this
12252      *  x.t:(test,tesT) 
12253      * 
12254      */
12255     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12256     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12257     
12258     iterChild : function (node, method) {
12259         
12260         var oldPre = this.inPre;
12261         if (node.tagName == 'PRE') {
12262             this.inPre = true;
12263         }
12264         for( var i = 0; i < node.childNodes.length; i++) {
12265             method.call(this, node.childNodes[i]);
12266         }
12267         this.inPre = oldPre;
12268     },
12269     
12270     
12271     
12272     /**
12273      * compile the template
12274      *
12275      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12276      *
12277      */
12278     compile: function()
12279     {
12280         var s = this.html;
12281         
12282         // covert the html into DOM...
12283         var doc = false;
12284         var div =false;
12285         try {
12286             doc = document.implementation.createHTMLDocument("");
12287             doc.documentElement.innerHTML =   this.html  ;
12288             div = doc.documentElement;
12289         } catch (e) {
12290             // old IE... - nasty -- it causes all sorts of issues.. with
12291             // images getting pulled from server..
12292             div = document.createElement('div');
12293             div.innerHTML = this.html;
12294         }
12295         //doc.documentElement.innerHTML = htmlBody
12296          
12297         
12298         
12299         this.tpls = [];
12300         var _t = this;
12301         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12302         
12303         var tpls = this.tpls;
12304         
12305         // create a top level template from the snippet..
12306         
12307         //Roo.log(div.innerHTML);
12308         
12309         var tpl = {
12310             uid : 'master',
12311             id : this.id++,
12312             attr : false,
12313             value : false,
12314             body : div.innerHTML,
12315             
12316             forCall : false,
12317             execCall : false,
12318             dom : div,
12319             isTop : true
12320             
12321         };
12322         tpls.unshift(tpl);
12323         
12324         
12325         // compile them...
12326         this.tpls = [];
12327         Roo.each(tpls, function(tp){
12328             this.compileTpl(tp);
12329             this.tpls[tp.id] = tp;
12330         }, this);
12331         
12332         this.master = tpls[0];
12333         return this;
12334         
12335         
12336     },
12337     
12338     compileNode : function(node, istop) {
12339         // test for
12340         //Roo.log(node);
12341         
12342         
12343         // skip anything not a tag..
12344         if (node.nodeType != 1) {
12345             if (node.nodeType == 3 && !this.inPre) {
12346                 // reduce white space..
12347                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12348                 
12349             }
12350             return;
12351         }
12352         
12353         var tpl = {
12354             uid : false,
12355             id : false,
12356             attr : false,
12357             value : false,
12358             body : '',
12359             
12360             forCall : false,
12361             execCall : false,
12362             dom : false,
12363             isTop : istop
12364             
12365             
12366         };
12367         
12368         
12369         switch(true) {
12370             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12371             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12372             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12373             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12374             // no default..
12375         }
12376         
12377         
12378         if (!tpl.attr) {
12379             // just itterate children..
12380             this.iterChild(node,this.compileNode);
12381             return;
12382         }
12383         tpl.uid = this.id++;
12384         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12385         node.removeAttribute('roo-'+ tpl.attr);
12386         if (tpl.attr != 'name') {
12387             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12388             node.parentNode.replaceChild(placeholder,  node);
12389         } else {
12390             
12391             var placeholder =  document.createElement('span');
12392             placeholder.className = 'roo-tpl-' + tpl.value;
12393             node.parentNode.replaceChild(placeholder,  node);
12394         }
12395         
12396         // parent now sees '{domtplXXXX}
12397         this.iterChild(node,this.compileNode);
12398         
12399         // we should now have node body...
12400         var div = document.createElement('div');
12401         div.appendChild(node);
12402         tpl.dom = node;
12403         // this has the unfortunate side effect of converting tagged attributes
12404         // eg. href="{...}" into %7C...%7D
12405         // this has been fixed by searching for those combo's although it's a bit hacky..
12406         
12407         
12408         tpl.body = div.innerHTML;
12409         
12410         
12411          
12412         tpl.id = tpl.uid;
12413         switch(tpl.attr) {
12414             case 'for' :
12415                 switch (tpl.value) {
12416                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12417                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12418                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12419                 }
12420                 break;
12421             
12422             case 'exec':
12423                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12424                 break;
12425             
12426             case 'if':     
12427                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12428                 break;
12429             
12430             case 'name':
12431                 tpl.id  = tpl.value; // replace non characters???
12432                 break;
12433             
12434         }
12435         
12436         
12437         this.tpls.push(tpl);
12438         
12439         
12440         
12441     },
12442     
12443     
12444     
12445     
12446     /**
12447      * Compile a segment of the template into a 'sub-template'
12448      *
12449      * 
12450      * 
12451      *
12452      */
12453     compileTpl : function(tpl)
12454     {
12455         var fm = Roo.util.Format;
12456         var useF = this.disableFormats !== true;
12457         
12458         var sep = Roo.isGecko ? "+\n" : ",\n";
12459         
12460         var undef = function(str) {
12461             Roo.debug && Roo.log("Property not found :"  + str);
12462             return '';
12463         };
12464           
12465         //Roo.log(tpl.body);
12466         
12467         
12468         
12469         var fn = function(m, lbrace, name, format, args)
12470         {
12471             //Roo.log("ARGS");
12472             //Roo.log(arguments);
12473             args = args ? args.replace(/\\'/g,"'") : args;
12474             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12475             if (typeof(format) == 'undefined') {
12476                 format =  'htmlEncode'; 
12477             }
12478             if (format == 'raw' ) {
12479                 format = false;
12480             }
12481             
12482             if(name.substr(0, 6) == 'domtpl'){
12483                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12484             }
12485             
12486             // build an array of options to determine if value is undefined..
12487             
12488             // basically get 'xxxx.yyyy' then do
12489             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12490             //    (function () { Roo.log("Property not found"); return ''; })() :
12491             //    ......
12492             
12493             var udef_ar = [];
12494             var lookfor = '';
12495             Roo.each(name.split('.'), function(st) {
12496                 lookfor += (lookfor.length ? '.': '') + st;
12497                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12498             });
12499             
12500             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12501             
12502             
12503             if(format && useF){
12504                 
12505                 args = args ? ',' + args : "";
12506                  
12507                 if(format.substr(0, 5) != "this."){
12508                     format = "fm." + format + '(';
12509                 }else{
12510                     format = 'this.call("'+ format.substr(5) + '", ';
12511                     args = ", values";
12512                 }
12513                 
12514                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12515             }
12516              
12517             if (args && args.length) {
12518                 // called with xxyx.yuu:(test,test)
12519                 // change to ()
12520                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12521             }
12522             // raw.. - :raw modifier..
12523             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12524             
12525         };
12526         var body;
12527         // branched to use + in gecko and [].join() in others
12528         if(Roo.isGecko){
12529             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12530                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12531                     "';};};";
12532         }else{
12533             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12534             body.push(tpl.body.replace(/(\r\n|\n)/g,
12535                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12536             body.push("'].join('');};};");
12537             body = body.join('');
12538         }
12539         
12540         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12541        
12542         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12543         eval(body);
12544         
12545         return this;
12546     },
12547      
12548     /**
12549      * same as applyTemplate, except it's done to one of the subTemplates
12550      * when using named templates, you can do:
12551      *
12552      * var str = pl.applySubTemplate('your-name', values);
12553      *
12554      * 
12555      * @param {Number} id of the template
12556      * @param {Object} values to apply to template
12557      * @param {Object} parent (normaly the instance of this object)
12558      */
12559     applySubTemplate : function(id, values, parent)
12560     {
12561         
12562         
12563         var t = this.tpls[id];
12564         
12565         
12566         try { 
12567             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12568                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12569                 return '';
12570             }
12571         } catch(e) {
12572             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12573             Roo.log(values);
12574           
12575             return '';
12576         }
12577         try { 
12578             
12579             if(t.execCall && t.execCall.call(this, values, parent)){
12580                 return '';
12581             }
12582         } catch(e) {
12583             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12584             Roo.log(values);
12585             return '';
12586         }
12587         
12588         try {
12589             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12590             parent = t.target ? values : parent;
12591             if(t.forCall && vs instanceof Array){
12592                 var buf = [];
12593                 for(var i = 0, len = vs.length; i < len; i++){
12594                     try {
12595                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12596                     } catch (e) {
12597                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12598                         Roo.log(e.body);
12599                         //Roo.log(t.compiled);
12600                         Roo.log(vs[i]);
12601                     }   
12602                 }
12603                 return buf.join('');
12604             }
12605         } catch (e) {
12606             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12607             Roo.log(values);
12608             return '';
12609         }
12610         try {
12611             return t.compiled.call(this, vs, parent);
12612         } catch (e) {
12613             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12614             Roo.log(e.body);
12615             //Roo.log(t.compiled);
12616             Roo.log(values);
12617             return '';
12618         }
12619     },
12620
12621    
12622
12623     applyTemplate : function(values){
12624         return this.master.compiled.call(this, values, {});
12625         //var s = this.subs;
12626     },
12627
12628     apply : function(){
12629         return this.applyTemplate.apply(this, arguments);
12630     }
12631
12632  });
12633
12634 Roo.DomTemplate.from = function(el){
12635     el = Roo.getDom(el);
12636     return new Roo.Domtemplate(el.value || el.innerHTML);
12637 };/*
12638  * Based on:
12639  * Ext JS Library 1.1.1
12640  * Copyright(c) 2006-2007, Ext JS, LLC.
12641  *
12642  * Originally Released Under LGPL - original licence link has changed is not relivant.
12643  *
12644  * Fork - LGPL
12645  * <script type="text/javascript">
12646  */
12647
12648 /**
12649  * @class Roo.util.DelayedTask
12650  * Provides a convenient method of performing setTimeout where a new
12651  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12652  * You can use this class to buffer
12653  * the keypress events for a certain number of milliseconds, and perform only if they stop
12654  * for that amount of time.
12655  * @constructor The parameters to this constructor serve as defaults and are not required.
12656  * @param {Function} fn (optional) The default function to timeout
12657  * @param {Object} scope (optional) The default scope of that timeout
12658  * @param {Array} args (optional) The default Array of arguments
12659  */
12660 Roo.util.DelayedTask = function(fn, scope, args){
12661     var id = null, d, t;
12662
12663     var call = function(){
12664         var now = new Date().getTime();
12665         if(now - t >= d){
12666             clearInterval(id);
12667             id = null;
12668             fn.apply(scope, args || []);
12669         }
12670     };
12671     /**
12672      * Cancels any pending timeout and queues a new one
12673      * @param {Number} delay The milliseconds to delay
12674      * @param {Function} newFn (optional) Overrides function passed to constructor
12675      * @param {Object} newScope (optional) Overrides scope passed to constructor
12676      * @param {Array} newArgs (optional) Overrides args passed to constructor
12677      */
12678     this.delay = function(delay, newFn, newScope, newArgs){
12679         if(id && delay != d){
12680             this.cancel();
12681         }
12682         d = delay;
12683         t = new Date().getTime();
12684         fn = newFn || fn;
12685         scope = newScope || scope;
12686         args = newArgs || args;
12687         if(!id){
12688             id = setInterval(call, d);
12689         }
12690     };
12691
12692     /**
12693      * Cancel the last queued timeout
12694      */
12695     this.cancel = function(){
12696         if(id){
12697             clearInterval(id);
12698             id = null;
12699         }
12700     };
12701 };/*
12702  * Based on:
12703  * Ext JS Library 1.1.1
12704  * Copyright(c) 2006-2007, Ext JS, LLC.
12705  *
12706  * Originally Released Under LGPL - original licence link has changed is not relivant.
12707  *
12708  * Fork - LGPL
12709  * <script type="text/javascript">
12710  */
12711  
12712  
12713 Roo.util.TaskRunner = function(interval){
12714     interval = interval || 10;
12715     var tasks = [], removeQueue = [];
12716     var id = 0;
12717     var running = false;
12718
12719     var stopThread = function(){
12720         running = false;
12721         clearInterval(id);
12722         id = 0;
12723     };
12724
12725     var startThread = function(){
12726         if(!running){
12727             running = true;
12728             id = setInterval(runTasks, interval);
12729         }
12730     };
12731
12732     var removeTask = function(task){
12733         removeQueue.push(task);
12734         if(task.onStop){
12735             task.onStop();
12736         }
12737     };
12738
12739     var runTasks = function(){
12740         if(removeQueue.length > 0){
12741             for(var i = 0, len = removeQueue.length; i < len; i++){
12742                 tasks.remove(removeQueue[i]);
12743             }
12744             removeQueue = [];
12745             if(tasks.length < 1){
12746                 stopThread();
12747                 return;
12748             }
12749         }
12750         var now = new Date().getTime();
12751         for(var i = 0, len = tasks.length; i < len; ++i){
12752             var t = tasks[i];
12753             var itime = now - t.taskRunTime;
12754             if(t.interval <= itime){
12755                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12756                 t.taskRunTime = now;
12757                 if(rt === false || t.taskRunCount === t.repeat){
12758                     removeTask(t);
12759                     return;
12760                 }
12761             }
12762             if(t.duration && t.duration <= (now - t.taskStartTime)){
12763                 removeTask(t);
12764             }
12765         }
12766     };
12767
12768     /**
12769      * Queues a new task.
12770      * @param {Object} task
12771      */
12772     this.start = function(task){
12773         tasks.push(task);
12774         task.taskStartTime = new Date().getTime();
12775         task.taskRunTime = 0;
12776         task.taskRunCount = 0;
12777         startThread();
12778         return task;
12779     };
12780
12781     this.stop = function(task){
12782         removeTask(task);
12783         return task;
12784     };
12785
12786     this.stopAll = function(){
12787         stopThread();
12788         for(var i = 0, len = tasks.length; i < len; i++){
12789             if(tasks[i].onStop){
12790                 tasks[i].onStop();
12791             }
12792         }
12793         tasks = [];
12794         removeQueue = [];
12795     };
12796 };
12797
12798 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12799  * Based on:
12800  * Ext JS Library 1.1.1
12801  * Copyright(c) 2006-2007, Ext JS, LLC.
12802  *
12803  * Originally Released Under LGPL - original licence link has changed is not relivant.
12804  *
12805  * Fork - LGPL
12806  * <script type="text/javascript">
12807  */
12808
12809  
12810 /**
12811  * @class Roo.util.MixedCollection
12812  * @extends Roo.util.Observable
12813  * A Collection class that maintains both numeric indexes and keys and exposes events.
12814  * @constructor
12815  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12816  * collection (defaults to false)
12817  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12818  * and return the key value for that item.  This is used when available to look up the key on items that
12819  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12820  * equivalent to providing an implementation for the {@link #getKey} method.
12821  */
12822 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12823     this.items = [];
12824     this.map = {};
12825     this.keys = [];
12826     this.length = 0;
12827     this.addEvents({
12828         /**
12829          * @event clear
12830          * Fires when the collection is cleared.
12831          */
12832         "clear" : true,
12833         /**
12834          * @event add
12835          * Fires when an item is added to the collection.
12836          * @param {Number} index The index at which the item was added.
12837          * @param {Object} o The item added.
12838          * @param {String} key The key associated with the added item.
12839          */
12840         "add" : true,
12841         /**
12842          * @event replace
12843          * Fires when an item is replaced in the collection.
12844          * @param {String} key he key associated with the new added.
12845          * @param {Object} old The item being replaced.
12846          * @param {Object} new The new item.
12847          */
12848         "replace" : true,
12849         /**
12850          * @event remove
12851          * Fires when an item is removed from the collection.
12852          * @param {Object} o The item being removed.
12853          * @param {String} key (optional) The key associated with the removed item.
12854          */
12855         "remove" : true,
12856         "sort" : true
12857     });
12858     this.allowFunctions = allowFunctions === true;
12859     if(keyFn){
12860         this.getKey = keyFn;
12861     }
12862     Roo.util.MixedCollection.superclass.constructor.call(this);
12863 };
12864
12865 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12866     allowFunctions : false,
12867     
12868 /**
12869  * Adds an item to the collection.
12870  * @param {String} key The key to associate with the item
12871  * @param {Object} o The item to add.
12872  * @return {Object} The item added.
12873  */
12874     add : function(key, o){
12875         if(arguments.length == 1){
12876             o = arguments[0];
12877             key = this.getKey(o);
12878         }
12879         if(typeof key == "undefined" || key === null){
12880             this.length++;
12881             this.items.push(o);
12882             this.keys.push(null);
12883         }else{
12884             var old = this.map[key];
12885             if(old){
12886                 return this.replace(key, o);
12887             }
12888             this.length++;
12889             this.items.push(o);
12890             this.map[key] = o;
12891             this.keys.push(key);
12892         }
12893         this.fireEvent("add", this.length-1, o, key);
12894         return o;
12895     },
12896        
12897 /**
12898   * MixedCollection has a generic way to fetch keys if you implement getKey.
12899 <pre><code>
12900 // normal way
12901 var mc = new Roo.util.MixedCollection();
12902 mc.add(someEl.dom.id, someEl);
12903 mc.add(otherEl.dom.id, otherEl);
12904 //and so on
12905
12906 // using getKey
12907 var mc = new Roo.util.MixedCollection();
12908 mc.getKey = function(el){
12909    return el.dom.id;
12910 };
12911 mc.add(someEl);
12912 mc.add(otherEl);
12913
12914 // or via the constructor
12915 var mc = new Roo.util.MixedCollection(false, function(el){
12916    return el.dom.id;
12917 });
12918 mc.add(someEl);
12919 mc.add(otherEl);
12920 </code></pre>
12921  * @param o {Object} The item for which to find the key.
12922  * @return {Object} The key for the passed item.
12923  */
12924     getKey : function(o){
12925          return o.id; 
12926     },
12927    
12928 /**
12929  * Replaces an item in the collection.
12930  * @param {String} key The key associated with the item to replace, or the item to replace.
12931  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12932  * @return {Object}  The new item.
12933  */
12934     replace : function(key, o){
12935         if(arguments.length == 1){
12936             o = arguments[0];
12937             key = this.getKey(o);
12938         }
12939         var old = this.item(key);
12940         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12941              return this.add(key, o);
12942         }
12943         var index = this.indexOfKey(key);
12944         this.items[index] = o;
12945         this.map[key] = o;
12946         this.fireEvent("replace", key, old, o);
12947         return o;
12948     },
12949    
12950 /**
12951  * Adds all elements of an Array or an Object to the collection.
12952  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12953  * an Array of values, each of which are added to the collection.
12954  */
12955     addAll : function(objs){
12956         if(arguments.length > 1 || objs instanceof Array){
12957             var args = arguments.length > 1 ? arguments : objs;
12958             for(var i = 0, len = args.length; i < len; i++){
12959                 this.add(args[i]);
12960             }
12961         }else{
12962             for(var key in objs){
12963                 if(this.allowFunctions || typeof objs[key] != "function"){
12964                     this.add(key, objs[key]);
12965                 }
12966             }
12967         }
12968     },
12969    
12970 /**
12971  * Executes the specified function once for every item in the collection, passing each
12972  * item as the first and only parameter. returning false from the function will stop the iteration.
12973  * @param {Function} fn The function to execute for each item.
12974  * @param {Object} scope (optional) The scope in which to execute the function.
12975  */
12976     each : function(fn, scope){
12977         var items = [].concat(this.items); // each safe for removal
12978         for(var i = 0, len = items.length; i < len; i++){
12979             if(fn.call(scope || items[i], items[i], i, len) === false){
12980                 break;
12981             }
12982         }
12983     },
12984    
12985 /**
12986  * Executes the specified function once for every key in the collection, passing each
12987  * key, and its associated item as the first two parameters.
12988  * @param {Function} fn The function to execute for each item.
12989  * @param {Object} scope (optional) The scope in which to execute the function.
12990  */
12991     eachKey : function(fn, scope){
12992         for(var i = 0, len = this.keys.length; i < len; i++){
12993             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12994         }
12995     },
12996    
12997 /**
12998  * Returns the first item in the collection which elicits a true return value from the
12999  * passed selection function.
13000  * @param {Function} fn The selection function to execute for each item.
13001  * @param {Object} scope (optional) The scope in which to execute the function.
13002  * @return {Object} The first item in the collection which returned true from the selection function.
13003  */
13004     find : function(fn, scope){
13005         for(var i = 0, len = this.items.length; i < len; i++){
13006             if(fn.call(scope || window, this.items[i], this.keys[i])){
13007                 return this.items[i];
13008             }
13009         }
13010         return null;
13011     },
13012    
13013 /**
13014  * Inserts an item at the specified index in the collection.
13015  * @param {Number} index The index to insert the item at.
13016  * @param {String} key The key to associate with the new item, or the item itself.
13017  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13018  * @return {Object} The item inserted.
13019  */
13020     insert : function(index, key, o){
13021         if(arguments.length == 2){
13022             o = arguments[1];
13023             key = this.getKey(o);
13024         }
13025         if(index >= this.length){
13026             return this.add(key, o);
13027         }
13028         this.length++;
13029         this.items.splice(index, 0, o);
13030         if(typeof key != "undefined" && key != null){
13031             this.map[key] = o;
13032         }
13033         this.keys.splice(index, 0, key);
13034         this.fireEvent("add", index, o, key);
13035         return o;
13036     },
13037    
13038 /**
13039  * Removed an item from the collection.
13040  * @param {Object} o The item to remove.
13041  * @return {Object} The item removed.
13042  */
13043     remove : function(o){
13044         return this.removeAt(this.indexOf(o));
13045     },
13046    
13047 /**
13048  * Remove an item from a specified index in the collection.
13049  * @param {Number} index The index within the collection of the item to remove.
13050  */
13051     removeAt : function(index){
13052         if(index < this.length && index >= 0){
13053             this.length--;
13054             var o = this.items[index];
13055             this.items.splice(index, 1);
13056             var key = this.keys[index];
13057             if(typeof key != "undefined"){
13058                 delete this.map[key];
13059             }
13060             this.keys.splice(index, 1);
13061             this.fireEvent("remove", o, key);
13062         }
13063     },
13064    
13065 /**
13066  * Removed an item associated with the passed key fom the collection.
13067  * @param {String} key The key of the item to remove.
13068  */
13069     removeKey : function(key){
13070         return this.removeAt(this.indexOfKey(key));
13071     },
13072    
13073 /**
13074  * Returns the number of items in the collection.
13075  * @return {Number} the number of items in the collection.
13076  */
13077     getCount : function(){
13078         return this.length; 
13079     },
13080    
13081 /**
13082  * Returns index within the collection of the passed Object.
13083  * @param {Object} o The item to find the index of.
13084  * @return {Number} index of the item.
13085  */
13086     indexOf : function(o){
13087         if(!this.items.indexOf){
13088             for(var i = 0, len = this.items.length; i < len; i++){
13089                 if(this.items[i] == o) return i;
13090             }
13091             return -1;
13092         }else{
13093             return this.items.indexOf(o);
13094         }
13095     },
13096    
13097 /**
13098  * Returns index within the collection of the passed key.
13099  * @param {String} key The key to find the index of.
13100  * @return {Number} index of the key.
13101  */
13102     indexOfKey : function(key){
13103         if(!this.keys.indexOf){
13104             for(var i = 0, len = this.keys.length; i < len; i++){
13105                 if(this.keys[i] == key) return i;
13106             }
13107             return -1;
13108         }else{
13109             return this.keys.indexOf(key);
13110         }
13111     },
13112    
13113 /**
13114  * Returns the item associated with the passed key OR index. Key has priority over index.
13115  * @param {String/Number} key The key or index of the item.
13116  * @return {Object} The item associated with the passed key.
13117  */
13118     item : function(key){
13119         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13120         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13121     },
13122     
13123 /**
13124  * Returns the item at the specified index.
13125  * @param {Number} index The index of the item.
13126  * @return {Object}
13127  */
13128     itemAt : function(index){
13129         return this.items[index];
13130     },
13131     
13132 /**
13133  * Returns the item associated with the passed key.
13134  * @param {String/Number} key The key of the item.
13135  * @return {Object} The item associated with the passed key.
13136  */
13137     key : function(key){
13138         return this.map[key];
13139     },
13140    
13141 /**
13142  * Returns true if the collection contains the passed Object as an item.
13143  * @param {Object} o  The Object to look for in the collection.
13144  * @return {Boolean} True if the collection contains the Object as an item.
13145  */
13146     contains : function(o){
13147         return this.indexOf(o) != -1;
13148     },
13149    
13150 /**
13151  * Returns true if the collection contains the passed Object as a key.
13152  * @param {String} key The key to look for in the collection.
13153  * @return {Boolean} True if the collection contains the Object as a key.
13154  */
13155     containsKey : function(key){
13156         return typeof this.map[key] != "undefined";
13157     },
13158    
13159 /**
13160  * Removes all items from the collection.
13161  */
13162     clear : function(){
13163         this.length = 0;
13164         this.items = [];
13165         this.keys = [];
13166         this.map = {};
13167         this.fireEvent("clear");
13168     },
13169    
13170 /**
13171  * Returns the first item in the collection.
13172  * @return {Object} the first item in the collection..
13173  */
13174     first : function(){
13175         return this.items[0]; 
13176     },
13177    
13178 /**
13179  * Returns the last item in the collection.
13180  * @return {Object} the last item in the collection..
13181  */
13182     last : function(){
13183         return this.items[this.length-1];   
13184     },
13185     
13186     _sort : function(property, dir, fn){
13187         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13188         fn = fn || function(a, b){
13189             return a-b;
13190         };
13191         var c = [], k = this.keys, items = this.items;
13192         for(var i = 0, len = items.length; i < len; i++){
13193             c[c.length] = {key: k[i], value: items[i], index: i};
13194         }
13195         c.sort(function(a, b){
13196             var v = fn(a[property], b[property]) * dsc;
13197             if(v == 0){
13198                 v = (a.index < b.index ? -1 : 1);
13199             }
13200             return v;
13201         });
13202         for(var i = 0, len = c.length; i < len; i++){
13203             items[i] = c[i].value;
13204             k[i] = c[i].key;
13205         }
13206         this.fireEvent("sort", this);
13207     },
13208     
13209     /**
13210      * Sorts this collection with the passed comparison function
13211      * @param {String} direction (optional) "ASC" or "DESC"
13212      * @param {Function} fn (optional) comparison function
13213      */
13214     sort : function(dir, fn){
13215         this._sort("value", dir, fn);
13216     },
13217     
13218     /**
13219      * Sorts this collection by keys
13220      * @param {String} direction (optional) "ASC" or "DESC"
13221      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13222      */
13223     keySort : function(dir, fn){
13224         this._sort("key", dir, fn || function(a, b){
13225             return String(a).toUpperCase()-String(b).toUpperCase();
13226         });
13227     },
13228     
13229     /**
13230      * Returns a range of items in this collection
13231      * @param {Number} startIndex (optional) defaults to 0
13232      * @param {Number} endIndex (optional) default to the last item
13233      * @return {Array} An array of items
13234      */
13235     getRange : function(start, end){
13236         var items = this.items;
13237         if(items.length < 1){
13238             return [];
13239         }
13240         start = start || 0;
13241         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13242         var r = [];
13243         if(start <= end){
13244             for(var i = start; i <= end; i++) {
13245                     r[r.length] = items[i];
13246             }
13247         }else{
13248             for(var i = start; i >= end; i--) {
13249                     r[r.length] = items[i];
13250             }
13251         }
13252         return r;
13253     },
13254         
13255     /**
13256      * Filter the <i>objects</i> in this collection by a specific property. 
13257      * Returns a new collection that has been filtered.
13258      * @param {String} property A property on your objects
13259      * @param {String/RegExp} value Either string that the property values 
13260      * should start with or a RegExp to test against the property
13261      * @return {MixedCollection} The new filtered collection
13262      */
13263     filter : function(property, value){
13264         if(!value.exec){ // not a regex
13265             value = String(value);
13266             if(value.length == 0){
13267                 return this.clone();
13268             }
13269             value = new RegExp("^" + Roo.escapeRe(value), "i");
13270         }
13271         return this.filterBy(function(o){
13272             return o && value.test(o[property]);
13273         });
13274         },
13275     
13276     /**
13277      * Filter by a function. * Returns a new collection that has been filtered.
13278      * The passed function will be called with each 
13279      * object in the collection. If the function returns true, the value is included 
13280      * otherwise it is filtered.
13281      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13282      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13283      * @return {MixedCollection} The new filtered collection
13284      */
13285     filterBy : function(fn, scope){
13286         var r = new Roo.util.MixedCollection();
13287         r.getKey = this.getKey;
13288         var k = this.keys, it = this.items;
13289         for(var i = 0, len = it.length; i < len; i++){
13290             if(fn.call(scope||this, it[i], k[i])){
13291                                 r.add(k[i], it[i]);
13292                         }
13293         }
13294         return r;
13295     },
13296     
13297     /**
13298      * Creates a duplicate of this collection
13299      * @return {MixedCollection}
13300      */
13301     clone : function(){
13302         var r = new Roo.util.MixedCollection();
13303         var k = this.keys, it = this.items;
13304         for(var i = 0, len = it.length; i < len; i++){
13305             r.add(k[i], it[i]);
13306         }
13307         r.getKey = this.getKey;
13308         return r;
13309     }
13310 });
13311 /**
13312  * Returns the item associated with the passed key or index.
13313  * @method
13314  * @param {String/Number} key The key or index of the item.
13315  * @return {Object} The item associated with the passed key.
13316  */
13317 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13318  * Based on:
13319  * Ext JS Library 1.1.1
13320  * Copyright(c) 2006-2007, Ext JS, LLC.
13321  *
13322  * Originally Released Under LGPL - original licence link has changed is not relivant.
13323  *
13324  * Fork - LGPL
13325  * <script type="text/javascript">
13326  */
13327 /**
13328  * @class Roo.util.JSON
13329  * Modified version of Douglas Crockford"s json.js that doesn"t
13330  * mess with the Object prototype 
13331  * http://www.json.org/js.html
13332  * @singleton
13333  */
13334 Roo.util.JSON = new (function(){
13335     var useHasOwn = {}.hasOwnProperty ? true : false;
13336     
13337     // crashes Safari in some instances
13338     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13339     
13340     var pad = function(n) {
13341         return n < 10 ? "0" + n : n;
13342     };
13343     
13344     var m = {
13345         "\b": '\\b',
13346         "\t": '\\t',
13347         "\n": '\\n',
13348         "\f": '\\f',
13349         "\r": '\\r',
13350         '"' : '\\"',
13351         "\\": '\\\\'
13352     };
13353
13354     var encodeString = function(s){
13355         if (/["\\\x00-\x1f]/.test(s)) {
13356             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13357                 var c = m[b];
13358                 if(c){
13359                     return c;
13360                 }
13361                 c = b.charCodeAt();
13362                 return "\\u00" +
13363                     Math.floor(c / 16).toString(16) +
13364                     (c % 16).toString(16);
13365             }) + '"';
13366         }
13367         return '"' + s + '"';
13368     };
13369     
13370     var encodeArray = function(o){
13371         var a = ["["], b, i, l = o.length, v;
13372             for (i = 0; i < l; i += 1) {
13373                 v = o[i];
13374                 switch (typeof v) {
13375                     case "undefined":
13376                     case "function":
13377                     case "unknown":
13378                         break;
13379                     default:
13380                         if (b) {
13381                             a.push(',');
13382                         }
13383                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13384                         b = true;
13385                 }
13386             }
13387             a.push("]");
13388             return a.join("");
13389     };
13390     
13391     var encodeDate = function(o){
13392         return '"' + o.getFullYear() + "-" +
13393                 pad(o.getMonth() + 1) + "-" +
13394                 pad(o.getDate()) + "T" +
13395                 pad(o.getHours()) + ":" +
13396                 pad(o.getMinutes()) + ":" +
13397                 pad(o.getSeconds()) + '"';
13398     };
13399     
13400     /**
13401      * Encodes an Object, Array or other value
13402      * @param {Mixed} o The variable to encode
13403      * @return {String} The JSON string
13404      */
13405     this.encode = function(o)
13406     {
13407         // should this be extended to fully wrap stringify..
13408         
13409         if(typeof o == "undefined" || o === null){
13410             return "null";
13411         }else if(o instanceof Array){
13412             return encodeArray(o);
13413         }else if(o instanceof Date){
13414             return encodeDate(o);
13415         }else if(typeof o == "string"){
13416             return encodeString(o);
13417         }else if(typeof o == "number"){
13418             return isFinite(o) ? String(o) : "null";
13419         }else if(typeof o == "boolean"){
13420             return String(o);
13421         }else {
13422             var a = ["{"], b, i, v;
13423             for (i in o) {
13424                 if(!useHasOwn || o.hasOwnProperty(i)) {
13425                     v = o[i];
13426                     switch (typeof v) {
13427                     case "undefined":
13428                     case "function":
13429                     case "unknown":
13430                         break;
13431                     default:
13432                         if(b){
13433                             a.push(',');
13434                         }
13435                         a.push(this.encode(i), ":",
13436                                 v === null ? "null" : this.encode(v));
13437                         b = true;
13438                     }
13439                 }
13440             }
13441             a.push("}");
13442             return a.join("");
13443         }
13444     };
13445     
13446     /**
13447      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13448      * @param {String} json The JSON string
13449      * @return {Object} The resulting object
13450      */
13451     this.decode = function(json){
13452         
13453         return  /** eval:var:json */ eval("(" + json + ')');
13454     };
13455 })();
13456 /** 
13457  * Shorthand for {@link Roo.util.JSON#encode}
13458  * @member Roo encode 
13459  * @method */
13460 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13461 /** 
13462  * Shorthand for {@link Roo.util.JSON#decode}
13463  * @member Roo decode 
13464  * @method */
13465 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13466 /*
13467  * Based on:
13468  * Ext JS Library 1.1.1
13469  * Copyright(c) 2006-2007, Ext JS, LLC.
13470  *
13471  * Originally Released Under LGPL - original licence link has changed is not relivant.
13472  *
13473  * Fork - LGPL
13474  * <script type="text/javascript">
13475  */
13476  
13477 /**
13478  * @class Roo.util.Format
13479  * Reusable data formatting functions
13480  * @singleton
13481  */
13482 Roo.util.Format = function(){
13483     var trimRe = /^\s+|\s+$/g;
13484     return {
13485         /**
13486          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13487          * @param {String} value The string to truncate
13488          * @param {Number} length The maximum length to allow before truncating
13489          * @return {String} The converted text
13490          */
13491         ellipsis : function(value, len){
13492             if(value && value.length > len){
13493                 return value.substr(0, len-3)+"...";
13494             }
13495             return value;
13496         },
13497
13498         /**
13499          * Checks a reference and converts it to empty string if it is undefined
13500          * @param {Mixed} value Reference to check
13501          * @return {Mixed} Empty string if converted, otherwise the original value
13502          */
13503         undef : function(value){
13504             return typeof value != "undefined" ? value : "";
13505         },
13506
13507         /**
13508          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13509          * @param {String} value The string to encode
13510          * @return {String} The encoded text
13511          */
13512         htmlEncode : function(value){
13513             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13514         },
13515
13516         /**
13517          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13518          * @param {String} value The string to decode
13519          * @return {String} The decoded text
13520          */
13521         htmlDecode : function(value){
13522             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13523         },
13524
13525         /**
13526          * Trims any whitespace from either side of a string
13527          * @param {String} value The text to trim
13528          * @return {String} The trimmed text
13529          */
13530         trim : function(value){
13531             return String(value).replace(trimRe, "");
13532         },
13533
13534         /**
13535          * Returns a substring from within an original string
13536          * @param {String} value The original text
13537          * @param {Number} start The start index of the substring
13538          * @param {Number} length The length of the substring
13539          * @return {String} The substring
13540          */
13541         substr : function(value, start, length){
13542             return String(value).substr(start, length);
13543         },
13544
13545         /**
13546          * Converts a string to all lower case letters
13547          * @param {String} value The text to convert
13548          * @return {String} The converted text
13549          */
13550         lowercase : function(value){
13551             return String(value).toLowerCase();
13552         },
13553
13554         /**
13555          * Converts a string to all upper case letters
13556          * @param {String} value The text to convert
13557          * @return {String} The converted text
13558          */
13559         uppercase : function(value){
13560             return String(value).toUpperCase();
13561         },
13562
13563         /**
13564          * Converts the first character only of a string to upper case
13565          * @param {String} value The text to convert
13566          * @return {String} The converted text
13567          */
13568         capitalize : function(value){
13569             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13570         },
13571
13572         // private
13573         call : function(value, fn){
13574             if(arguments.length > 2){
13575                 var args = Array.prototype.slice.call(arguments, 2);
13576                 args.unshift(value);
13577                  
13578                 return /** eval:var:value */  eval(fn).apply(window, args);
13579             }else{
13580                 /** eval:var:value */
13581                 return /** eval:var:value */ eval(fn).call(window, value);
13582             }
13583         },
13584
13585        
13586         /**
13587          * safer version of Math.toFixed..??/
13588          * @param {Number/String} value The numeric value to format
13589          * @param {Number/String} value Decimal places 
13590          * @return {String} The formatted currency string
13591          */
13592         toFixed : function(v, n)
13593         {
13594             // why not use to fixed - precision is buggered???
13595             if (!n) {
13596                 return Math.round(v-0);
13597             }
13598             var fact = Math.pow(10,n+1);
13599             v = (Math.round((v-0)*fact))/fact;
13600             var z = (''+fact).substring(2);
13601             if (v == Math.floor(v)) {
13602                 return Math.floor(v) + '.' + z;
13603             }
13604             
13605             // now just padd decimals..
13606             var ps = String(v).split('.');
13607             var fd = (ps[1] + z);
13608             var r = fd.substring(0,n); 
13609             var rm = fd.substring(n); 
13610             if (rm < 5) {
13611                 return ps[0] + '.' + r;
13612             }
13613             r*=1; // turn it into a number;
13614             r++;
13615             if (String(r).length != n) {
13616                 ps[0]*=1;
13617                 ps[0]++;
13618                 r = String(r).substring(1); // chop the end off.
13619             }
13620             
13621             return ps[0] + '.' + r;
13622              
13623         },
13624         
13625         /**
13626          * Format a number as US currency
13627          * @param {Number/String} value The numeric value to format
13628          * @return {String} The formatted currency string
13629          */
13630         usMoney : function(v){
13631             return '$' + Roo.util.Format.number(v);
13632         },
13633         
13634         /**
13635          * Format a number
13636          * eventually this should probably emulate php's number_format
13637          * @param {Number/String} value The numeric value to format
13638          * @param {Number} decimals number of decimal places
13639          * @return {String} The formatted currency string
13640          */
13641         number : function(v,decimals)
13642         {
13643             // multiply and round.
13644             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13645             var mul = Math.pow(10, decimals);
13646             var zero = String(mul).substring(1);
13647             v = (Math.round((v-0)*mul))/mul;
13648             
13649             // if it's '0' number.. then
13650             
13651             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13652             v = String(v);
13653             var ps = v.split('.');
13654             var whole = ps[0];
13655             
13656             
13657             var r = /(\d+)(\d{3})/;
13658             // add comma's
13659             while (r.test(whole)) {
13660                 whole = whole.replace(r, '$1' + ',' + '$2');
13661             }
13662             
13663             
13664             var sub = ps[1] ?
13665                     // has decimals..
13666                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13667                     // does not have decimals
13668                     (decimals ? ('.' + zero) : '');
13669             
13670             
13671             return whole + sub ;
13672         },
13673         
13674         /**
13675          * Parse a value into a formatted date using the specified format pattern.
13676          * @param {Mixed} value The value to format
13677          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13678          * @return {String} The formatted date string
13679          */
13680         date : function(v, format){
13681             if(!v){
13682                 return "";
13683             }
13684             if(!(v instanceof Date)){
13685                 v = new Date(Date.parse(v));
13686             }
13687             return v.dateFormat(format || Roo.util.Format.defaults.date);
13688         },
13689
13690         /**
13691          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13692          * @param {String} format Any valid date format string
13693          * @return {Function} The date formatting function
13694          */
13695         dateRenderer : function(format){
13696             return function(v){
13697                 return Roo.util.Format.date(v, format);  
13698             };
13699         },
13700
13701         // private
13702         stripTagsRE : /<\/?[^>]+>/gi,
13703         
13704         /**
13705          * Strips all HTML tags
13706          * @param {Mixed} value The text from which to strip tags
13707          * @return {String} The stripped text
13708          */
13709         stripTags : function(v){
13710             return !v ? v : String(v).replace(this.stripTagsRE, "");
13711         }
13712     };
13713 }();
13714 Roo.util.Format.defaults = {
13715     date : 'd/M/Y'
13716 };/*
13717  * Based on:
13718  * Ext JS Library 1.1.1
13719  * Copyright(c) 2006-2007, Ext JS, LLC.
13720  *
13721  * Originally Released Under LGPL - original licence link has changed is not relivant.
13722  *
13723  * Fork - LGPL
13724  * <script type="text/javascript">
13725  */
13726
13727
13728  
13729
13730 /**
13731  * @class Roo.MasterTemplate
13732  * @extends Roo.Template
13733  * Provides a template that can have child templates. The syntax is:
13734 <pre><code>
13735 var t = new Roo.MasterTemplate(
13736         '&lt;select name="{name}"&gt;',
13737                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13738         '&lt;/select&gt;'
13739 );
13740 t.add('options', {value: 'foo', text: 'bar'});
13741 // or you can add multiple child elements in one shot
13742 t.addAll('options', [
13743     {value: 'foo', text: 'bar'},
13744     {value: 'foo2', text: 'bar2'},
13745     {value: 'foo3', text: 'bar3'}
13746 ]);
13747 // then append, applying the master template values
13748 t.append('my-form', {name: 'my-select'});
13749 </code></pre>
13750 * A name attribute for the child template is not required if you have only one child
13751 * template or you want to refer to them by index.
13752  */
13753 Roo.MasterTemplate = function(){
13754     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13755     this.originalHtml = this.html;
13756     var st = {};
13757     var m, re = this.subTemplateRe;
13758     re.lastIndex = 0;
13759     var subIndex = 0;
13760     while(m = re.exec(this.html)){
13761         var name = m[1], content = m[2];
13762         st[subIndex] = {
13763             name: name,
13764             index: subIndex,
13765             buffer: [],
13766             tpl : new Roo.Template(content)
13767         };
13768         if(name){
13769             st[name] = st[subIndex];
13770         }
13771         st[subIndex].tpl.compile();
13772         st[subIndex].tpl.call = this.call.createDelegate(this);
13773         subIndex++;
13774     }
13775     this.subCount = subIndex;
13776     this.subs = st;
13777 };
13778 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13779     /**
13780     * The regular expression used to match sub templates
13781     * @type RegExp
13782     * @property
13783     */
13784     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13785
13786     /**
13787      * Applies the passed values to a child template.
13788      * @param {String/Number} name (optional) The name or index of the child template
13789      * @param {Array/Object} values The values to be applied to the template
13790      * @return {MasterTemplate} this
13791      */
13792      add : function(name, values){
13793         if(arguments.length == 1){
13794             values = arguments[0];
13795             name = 0;
13796         }
13797         var s = this.subs[name];
13798         s.buffer[s.buffer.length] = s.tpl.apply(values);
13799         return this;
13800     },
13801
13802     /**
13803      * Applies all the passed values to a child template.
13804      * @param {String/Number} name (optional) The name or index of the child template
13805      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13806      * @param {Boolean} reset (optional) True to reset the template first
13807      * @return {MasterTemplate} this
13808      */
13809     fill : function(name, values, reset){
13810         var a = arguments;
13811         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13812             values = a[0];
13813             name = 0;
13814             reset = a[1];
13815         }
13816         if(reset){
13817             this.reset();
13818         }
13819         for(var i = 0, len = values.length; i < len; i++){
13820             this.add(name, values[i]);
13821         }
13822         return this;
13823     },
13824
13825     /**
13826      * Resets the template for reuse
13827      * @return {MasterTemplate} this
13828      */
13829      reset : function(){
13830         var s = this.subs;
13831         for(var i = 0; i < this.subCount; i++){
13832             s[i].buffer = [];
13833         }
13834         return this;
13835     },
13836
13837     applyTemplate : function(values){
13838         var s = this.subs;
13839         var replaceIndex = -1;
13840         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13841             return s[++replaceIndex].buffer.join("");
13842         });
13843         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13844     },
13845
13846     apply : function(){
13847         return this.applyTemplate.apply(this, arguments);
13848     },
13849
13850     compile : function(){return this;}
13851 });
13852
13853 /**
13854  * Alias for fill().
13855  * @method
13856  */
13857 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13858  /**
13859  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13860  * var tpl = Roo.MasterTemplate.from('element-id');
13861  * @param {String/HTMLElement} el
13862  * @param {Object} config
13863  * @static
13864  */
13865 Roo.MasterTemplate.from = function(el, config){
13866     el = Roo.getDom(el);
13867     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13868 };/*
13869  * Based on:
13870  * Ext JS Library 1.1.1
13871  * Copyright(c) 2006-2007, Ext JS, LLC.
13872  *
13873  * Originally Released Under LGPL - original licence link has changed is not relivant.
13874  *
13875  * Fork - LGPL
13876  * <script type="text/javascript">
13877  */
13878
13879  
13880 /**
13881  * @class Roo.util.CSS
13882  * Utility class for manipulating CSS rules
13883  * @singleton
13884  */
13885 Roo.util.CSS = function(){
13886         var rules = null;
13887         var doc = document;
13888
13889     var camelRe = /(-[a-z])/gi;
13890     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13891
13892    return {
13893    /**
13894     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13895     * tag and appended to the HEAD of the document.
13896     * @param {String|Object} cssText The text containing the css rules
13897     * @param {String} id An id to add to the stylesheet for later removal
13898     * @return {StyleSheet}
13899     */
13900     createStyleSheet : function(cssText, id){
13901         var ss;
13902         var head = doc.getElementsByTagName("head")[0];
13903         var nrules = doc.createElement("style");
13904         nrules.setAttribute("type", "text/css");
13905         if(id){
13906             nrules.setAttribute("id", id);
13907         }
13908         if (typeof(cssText) != 'string') {
13909             // support object maps..
13910             // not sure if this a good idea.. 
13911             // perhaps it should be merged with the general css handling
13912             // and handle js style props.
13913             var cssTextNew = [];
13914             for(var n in cssText) {
13915                 var citems = [];
13916                 for(var k in cssText[n]) {
13917                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13918                 }
13919                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13920                 
13921             }
13922             cssText = cssTextNew.join("\n");
13923             
13924         }
13925        
13926        
13927        if(Roo.isIE){
13928            head.appendChild(nrules);
13929            ss = nrules.styleSheet;
13930            ss.cssText = cssText;
13931        }else{
13932            try{
13933                 nrules.appendChild(doc.createTextNode(cssText));
13934            }catch(e){
13935                nrules.cssText = cssText; 
13936            }
13937            head.appendChild(nrules);
13938            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13939        }
13940        this.cacheStyleSheet(ss);
13941        return ss;
13942    },
13943
13944    /**
13945     * Removes a style or link tag by id
13946     * @param {String} id The id of the tag
13947     */
13948    removeStyleSheet : function(id){
13949        var existing = doc.getElementById(id);
13950        if(existing){
13951            existing.parentNode.removeChild(existing);
13952        }
13953    },
13954
13955    /**
13956     * Dynamically swaps an existing stylesheet reference for a new one
13957     * @param {String} id The id of an existing link tag to remove
13958     * @param {String} url The href of the new stylesheet to include
13959     */
13960    swapStyleSheet : function(id, url){
13961        this.removeStyleSheet(id);
13962        var ss = doc.createElement("link");
13963        ss.setAttribute("rel", "stylesheet");
13964        ss.setAttribute("type", "text/css");
13965        ss.setAttribute("id", id);
13966        ss.setAttribute("href", url);
13967        doc.getElementsByTagName("head")[0].appendChild(ss);
13968    },
13969    
13970    /**
13971     * Refresh the rule cache if you have dynamically added stylesheets
13972     * @return {Object} An object (hash) of rules indexed by selector
13973     */
13974    refreshCache : function(){
13975        return this.getRules(true);
13976    },
13977
13978    // private
13979    cacheStyleSheet : function(stylesheet){
13980        if(!rules){
13981            rules = {};
13982        }
13983        try{// try catch for cross domain access issue
13984            var ssRules = stylesheet.cssRules || stylesheet.rules;
13985            for(var j = ssRules.length-1; j >= 0; --j){
13986                rules[ssRules[j].selectorText] = ssRules[j];
13987            }
13988        }catch(e){}
13989    },
13990    
13991    /**
13992     * Gets all css rules for the document
13993     * @param {Boolean} refreshCache true to refresh the internal cache
13994     * @return {Object} An object (hash) of rules indexed by selector
13995     */
13996    getRules : function(refreshCache){
13997                 if(rules == null || refreshCache){
13998                         rules = {};
13999                         var ds = doc.styleSheets;
14000                         for(var i =0, len = ds.length; i < len; i++){
14001                             try{
14002                         this.cacheStyleSheet(ds[i]);
14003                     }catch(e){} 
14004                 }
14005                 }
14006                 return rules;
14007         },
14008         
14009         /**
14010     * Gets an an individual CSS rule by selector(s)
14011     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14012     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14013     * @return {CSSRule} The CSS rule or null if one is not found
14014     */
14015    getRule : function(selector, refreshCache){
14016                 var rs = this.getRules(refreshCache);
14017                 if(!(selector instanceof Array)){
14018                     return rs[selector];
14019                 }
14020                 for(var i = 0; i < selector.length; i++){
14021                         if(rs[selector[i]]){
14022                                 return rs[selector[i]];
14023                         }
14024                 }
14025                 return null;
14026         },
14027         
14028         
14029         /**
14030     * Updates a rule property
14031     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14032     * @param {String} property The css property
14033     * @param {String} value The new value for the property
14034     * @return {Boolean} true If a rule was found and updated
14035     */
14036    updateRule : function(selector, property, value){
14037                 if(!(selector instanceof Array)){
14038                         var rule = this.getRule(selector);
14039                         if(rule){
14040                                 rule.style[property.replace(camelRe, camelFn)] = value;
14041                                 return true;
14042                         }
14043                 }else{
14044                         for(var i = 0; i < selector.length; i++){
14045                                 if(this.updateRule(selector[i], property, value)){
14046                                         return true;
14047                                 }
14048                         }
14049                 }
14050                 return false;
14051         }
14052    };   
14053 }();/*
14054  * Based on:
14055  * Ext JS Library 1.1.1
14056  * Copyright(c) 2006-2007, Ext JS, LLC.
14057  *
14058  * Originally Released Under LGPL - original licence link has changed is not relivant.
14059  *
14060  * Fork - LGPL
14061  * <script type="text/javascript">
14062  */
14063
14064  
14065
14066 /**
14067  * @class Roo.util.ClickRepeater
14068  * @extends Roo.util.Observable
14069  * 
14070  * A wrapper class which can be applied to any element. Fires a "click" event while the
14071  * mouse is pressed. The interval between firings may be specified in the config but
14072  * defaults to 10 milliseconds.
14073  * 
14074  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14075  * 
14076  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14077  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14078  * Similar to an autorepeat key delay.
14079  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14080  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14081  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14082  *           "interval" and "delay" are ignored. "immediate" is honored.
14083  * @cfg {Boolean} preventDefault True to prevent the default click event
14084  * @cfg {Boolean} stopDefault True to stop the default click event
14085  * 
14086  * @history
14087  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14088  *     2007-02-02 jvs Renamed to ClickRepeater
14089  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14090  *
14091  *  @constructor
14092  * @param {String/HTMLElement/Element} el The element to listen on
14093  * @param {Object} config
14094  **/
14095 Roo.util.ClickRepeater = function(el, config)
14096 {
14097     this.el = Roo.get(el);
14098     this.el.unselectable();
14099
14100     Roo.apply(this, config);
14101
14102     this.addEvents({
14103     /**
14104      * @event mousedown
14105      * Fires when the mouse button is depressed.
14106      * @param {Roo.util.ClickRepeater} this
14107      */
14108         "mousedown" : true,
14109     /**
14110      * @event click
14111      * Fires on a specified interval during the time the element is pressed.
14112      * @param {Roo.util.ClickRepeater} this
14113      */
14114         "click" : true,
14115     /**
14116      * @event mouseup
14117      * Fires when the mouse key is released.
14118      * @param {Roo.util.ClickRepeater} this
14119      */
14120         "mouseup" : true
14121     });
14122
14123     this.el.on("mousedown", this.handleMouseDown, this);
14124     if(this.preventDefault || this.stopDefault){
14125         this.el.on("click", function(e){
14126             if(this.preventDefault){
14127                 e.preventDefault();
14128             }
14129             if(this.stopDefault){
14130                 e.stopEvent();
14131             }
14132         }, this);
14133     }
14134
14135     // allow inline handler
14136     if(this.handler){
14137         this.on("click", this.handler,  this.scope || this);
14138     }
14139
14140     Roo.util.ClickRepeater.superclass.constructor.call(this);
14141 };
14142
14143 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14144     interval : 20,
14145     delay: 250,
14146     preventDefault : true,
14147     stopDefault : false,
14148     timer : 0,
14149
14150     // private
14151     handleMouseDown : function(){
14152         clearTimeout(this.timer);
14153         this.el.blur();
14154         if(this.pressClass){
14155             this.el.addClass(this.pressClass);
14156         }
14157         this.mousedownTime = new Date();
14158
14159         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14160         this.el.on("mouseout", this.handleMouseOut, this);
14161
14162         this.fireEvent("mousedown", this);
14163         this.fireEvent("click", this);
14164         
14165         this.timer = this.click.defer(this.delay || this.interval, this);
14166     },
14167
14168     // private
14169     click : function(){
14170         this.fireEvent("click", this);
14171         this.timer = this.click.defer(this.getInterval(), this);
14172     },
14173
14174     // private
14175     getInterval: function(){
14176         if(!this.accelerate){
14177             return this.interval;
14178         }
14179         var pressTime = this.mousedownTime.getElapsed();
14180         if(pressTime < 500){
14181             return 400;
14182         }else if(pressTime < 1700){
14183             return 320;
14184         }else if(pressTime < 2600){
14185             return 250;
14186         }else if(pressTime < 3500){
14187             return 180;
14188         }else if(pressTime < 4400){
14189             return 140;
14190         }else if(pressTime < 5300){
14191             return 80;
14192         }else if(pressTime < 6200){
14193             return 50;
14194         }else{
14195             return 10;
14196         }
14197     },
14198
14199     // private
14200     handleMouseOut : function(){
14201         clearTimeout(this.timer);
14202         if(this.pressClass){
14203             this.el.removeClass(this.pressClass);
14204         }
14205         this.el.on("mouseover", this.handleMouseReturn, this);
14206     },
14207
14208     // private
14209     handleMouseReturn : function(){
14210         this.el.un("mouseover", this.handleMouseReturn);
14211         if(this.pressClass){
14212             this.el.addClass(this.pressClass);
14213         }
14214         this.click();
14215     },
14216
14217     // private
14218     handleMouseUp : function(){
14219         clearTimeout(this.timer);
14220         this.el.un("mouseover", this.handleMouseReturn);
14221         this.el.un("mouseout", this.handleMouseOut);
14222         Roo.get(document).un("mouseup", this.handleMouseUp);
14223         this.el.removeClass(this.pressClass);
14224         this.fireEvent("mouseup", this);
14225     }
14226 });/*
14227  * Based on:
14228  * Ext JS Library 1.1.1
14229  * Copyright(c) 2006-2007, Ext JS, LLC.
14230  *
14231  * Originally Released Under LGPL - original licence link has changed is not relivant.
14232  *
14233  * Fork - LGPL
14234  * <script type="text/javascript">
14235  */
14236
14237  
14238 /**
14239  * @class Roo.KeyNav
14240  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14241  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14242  * way to implement custom navigation schemes for any UI component.</p>
14243  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14244  * pageUp, pageDown, del, home, end.  Usage:</p>
14245  <pre><code>
14246 var nav = new Roo.KeyNav("my-element", {
14247     "left" : function(e){
14248         this.moveLeft(e.ctrlKey);
14249     },
14250     "right" : function(e){
14251         this.moveRight(e.ctrlKey);
14252     },
14253     "enter" : function(e){
14254         this.save();
14255     },
14256     scope : this
14257 });
14258 </code></pre>
14259  * @constructor
14260  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14261  * @param {Object} config The config
14262  */
14263 Roo.KeyNav = function(el, config){
14264     this.el = Roo.get(el);
14265     Roo.apply(this, config);
14266     if(!this.disabled){
14267         this.disabled = true;
14268         this.enable();
14269     }
14270 };
14271
14272 Roo.KeyNav.prototype = {
14273     /**
14274      * @cfg {Boolean} disabled
14275      * True to disable this KeyNav instance (defaults to false)
14276      */
14277     disabled : false,
14278     /**
14279      * @cfg {String} defaultEventAction
14280      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14281      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14282      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14283      */
14284     defaultEventAction: "stopEvent",
14285     /**
14286      * @cfg {Boolean} forceKeyDown
14287      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14288      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14289      * handle keydown instead of keypress.
14290      */
14291     forceKeyDown : false,
14292
14293     // private
14294     prepareEvent : function(e){
14295         var k = e.getKey();
14296         var h = this.keyToHandler[k];
14297         //if(h && this[h]){
14298         //    e.stopPropagation();
14299         //}
14300         if(Roo.isSafari && h && k >= 37 && k <= 40){
14301             e.stopEvent();
14302         }
14303     },
14304
14305     // private
14306     relay : function(e){
14307         var k = e.getKey();
14308         var h = this.keyToHandler[k];
14309         if(h && this[h]){
14310             if(this.doRelay(e, this[h], h) !== true){
14311                 e[this.defaultEventAction]();
14312             }
14313         }
14314     },
14315
14316     // private
14317     doRelay : function(e, h, hname){
14318         return h.call(this.scope || this, e);
14319     },
14320
14321     // possible handlers
14322     enter : false,
14323     left : false,
14324     right : false,
14325     up : false,
14326     down : false,
14327     tab : false,
14328     esc : false,
14329     pageUp : false,
14330     pageDown : false,
14331     del : false,
14332     home : false,
14333     end : false,
14334
14335     // quick lookup hash
14336     keyToHandler : {
14337         37 : "left",
14338         39 : "right",
14339         38 : "up",
14340         40 : "down",
14341         33 : "pageUp",
14342         34 : "pageDown",
14343         46 : "del",
14344         36 : "home",
14345         35 : "end",
14346         13 : "enter",
14347         27 : "esc",
14348         9  : "tab"
14349     },
14350
14351         /**
14352          * Enable this KeyNav
14353          */
14354         enable: function(){
14355                 if(this.disabled){
14356             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14357             // the EventObject will normalize Safari automatically
14358             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14359                 this.el.on("keydown", this.relay,  this);
14360             }else{
14361                 this.el.on("keydown", this.prepareEvent,  this);
14362                 this.el.on("keypress", this.relay,  this);
14363             }
14364                     this.disabled = false;
14365                 }
14366         },
14367
14368         /**
14369          * Disable this KeyNav
14370          */
14371         disable: function(){
14372                 if(!this.disabled){
14373                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14374                 this.el.un("keydown", this.relay);
14375             }else{
14376                 this.el.un("keydown", this.prepareEvent);
14377                 this.el.un("keypress", this.relay);
14378             }
14379                     this.disabled = true;
14380                 }
14381         }
14382 };/*
14383  * Based on:
14384  * Ext JS Library 1.1.1
14385  * Copyright(c) 2006-2007, Ext JS, LLC.
14386  *
14387  * Originally Released Under LGPL - original licence link has changed is not relivant.
14388  *
14389  * Fork - LGPL
14390  * <script type="text/javascript">
14391  */
14392
14393  
14394 /**
14395  * @class Roo.KeyMap
14396  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14397  * The constructor accepts the same config object as defined by {@link #addBinding}.
14398  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14399  * combination it will call the function with this signature (if the match is a multi-key
14400  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14401  * A KeyMap can also handle a string representation of keys.<br />
14402  * Usage:
14403  <pre><code>
14404 // map one key by key code
14405 var map = new Roo.KeyMap("my-element", {
14406     key: 13, // or Roo.EventObject.ENTER
14407     fn: myHandler,
14408     scope: myObject
14409 });
14410
14411 // map multiple keys to one action by string
14412 var map = new Roo.KeyMap("my-element", {
14413     key: "a\r\n\t",
14414     fn: myHandler,
14415     scope: myObject
14416 });
14417
14418 // map multiple keys to multiple actions by strings and array of codes
14419 var map = new Roo.KeyMap("my-element", [
14420     {
14421         key: [10,13],
14422         fn: function(){ alert("Return was pressed"); }
14423     }, {
14424         key: "abc",
14425         fn: function(){ alert('a, b or c was pressed'); }
14426     }, {
14427         key: "\t",
14428         ctrl:true,
14429         shift:true,
14430         fn: function(){ alert('Control + shift + tab was pressed.'); }
14431     }
14432 ]);
14433 </code></pre>
14434  * <b>Note: A KeyMap starts enabled</b>
14435  * @constructor
14436  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14437  * @param {Object} config The config (see {@link #addBinding})
14438  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14439  */
14440 Roo.KeyMap = function(el, config, eventName){
14441     this.el  = Roo.get(el);
14442     this.eventName = eventName || "keydown";
14443     this.bindings = [];
14444     if(config){
14445         this.addBinding(config);
14446     }
14447     this.enable();
14448 };
14449
14450 Roo.KeyMap.prototype = {
14451     /**
14452      * True to stop the event from bubbling and prevent the default browser action if the
14453      * key was handled by the KeyMap (defaults to false)
14454      * @type Boolean
14455      */
14456     stopEvent : false,
14457
14458     /**
14459      * Add a new binding to this KeyMap. The following config object properties are supported:
14460      * <pre>
14461 Property    Type             Description
14462 ----------  ---------------  ----------------------------------------------------------------------
14463 key         String/Array     A single keycode or an array of keycodes to handle
14464 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14465 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14466 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14467 fn          Function         The function to call when KeyMap finds the expected key combination
14468 scope       Object           The scope of the callback function
14469 </pre>
14470      *
14471      * Usage:
14472      * <pre><code>
14473 // Create a KeyMap
14474 var map = new Roo.KeyMap(document, {
14475     key: Roo.EventObject.ENTER,
14476     fn: handleKey,
14477     scope: this
14478 });
14479
14480 //Add a new binding to the existing KeyMap later
14481 map.addBinding({
14482     key: 'abc',
14483     shift: true,
14484     fn: handleKey,
14485     scope: this
14486 });
14487 </code></pre>
14488      * @param {Object/Array} config A single KeyMap config or an array of configs
14489      */
14490         addBinding : function(config){
14491         if(config instanceof Array){
14492             for(var i = 0, len = config.length; i < len; i++){
14493                 this.addBinding(config[i]);
14494             }
14495             return;
14496         }
14497         var keyCode = config.key,
14498             shift = config.shift, 
14499             ctrl = config.ctrl, 
14500             alt = config.alt,
14501             fn = config.fn,
14502             scope = config.scope;
14503         if(typeof keyCode == "string"){
14504             var ks = [];
14505             var keyString = keyCode.toUpperCase();
14506             for(var j = 0, len = keyString.length; j < len; j++){
14507                 ks.push(keyString.charCodeAt(j));
14508             }
14509             keyCode = ks;
14510         }
14511         var keyArray = keyCode instanceof Array;
14512         var handler = function(e){
14513             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14514                 var k = e.getKey();
14515                 if(keyArray){
14516                     for(var i = 0, len = keyCode.length; i < len; i++){
14517                         if(keyCode[i] == k){
14518                           if(this.stopEvent){
14519                               e.stopEvent();
14520                           }
14521                           fn.call(scope || window, k, e);
14522                           return;
14523                         }
14524                     }
14525                 }else{
14526                     if(k == keyCode){
14527                         if(this.stopEvent){
14528                            e.stopEvent();
14529                         }
14530                         fn.call(scope || window, k, e);
14531                     }
14532                 }
14533             }
14534         };
14535         this.bindings.push(handler);  
14536         },
14537
14538     /**
14539      * Shorthand for adding a single key listener
14540      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14541      * following options:
14542      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14543      * @param {Function} fn The function to call
14544      * @param {Object} scope (optional) The scope of the function
14545      */
14546     on : function(key, fn, scope){
14547         var keyCode, shift, ctrl, alt;
14548         if(typeof key == "object" && !(key instanceof Array)){
14549             keyCode = key.key;
14550             shift = key.shift;
14551             ctrl = key.ctrl;
14552             alt = key.alt;
14553         }else{
14554             keyCode = key;
14555         }
14556         this.addBinding({
14557             key: keyCode,
14558             shift: shift,
14559             ctrl: ctrl,
14560             alt: alt,
14561             fn: fn,
14562             scope: scope
14563         })
14564     },
14565
14566     // private
14567     handleKeyDown : function(e){
14568             if(this.enabled){ //just in case
14569             var b = this.bindings;
14570             for(var i = 0, len = b.length; i < len; i++){
14571                 b[i].call(this, e);
14572             }
14573             }
14574         },
14575         
14576         /**
14577          * Returns true if this KeyMap is enabled
14578          * @return {Boolean} 
14579          */
14580         isEnabled : function(){
14581             return this.enabled;  
14582         },
14583         
14584         /**
14585          * Enables this KeyMap
14586          */
14587         enable: function(){
14588                 if(!this.enabled){
14589                     this.el.on(this.eventName, this.handleKeyDown, this);
14590                     this.enabled = true;
14591                 }
14592         },
14593
14594         /**
14595          * Disable this KeyMap
14596          */
14597         disable: function(){
14598                 if(this.enabled){
14599                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14600                     this.enabled = false;
14601                 }
14602         }
14603 };/*
14604  * Based on:
14605  * Ext JS Library 1.1.1
14606  * Copyright(c) 2006-2007, Ext JS, LLC.
14607  *
14608  * Originally Released Under LGPL - original licence link has changed is not relivant.
14609  *
14610  * Fork - LGPL
14611  * <script type="text/javascript">
14612  */
14613
14614  
14615 /**
14616  * @class Roo.util.TextMetrics
14617  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14618  * wide, in pixels, a given block of text will be.
14619  * @singleton
14620  */
14621 Roo.util.TextMetrics = function(){
14622     var shared;
14623     return {
14624         /**
14625          * Measures the size of the specified text
14626          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14627          * that can affect the size of the rendered text
14628          * @param {String} text The text to measure
14629          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14630          * in order to accurately measure the text height
14631          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14632          */
14633         measure : function(el, text, fixedWidth){
14634             if(!shared){
14635                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14636             }
14637             shared.bind(el);
14638             shared.setFixedWidth(fixedWidth || 'auto');
14639             return shared.getSize(text);
14640         },
14641
14642         /**
14643          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14644          * the overhead of multiple calls to initialize the style properties on each measurement.
14645          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14646          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14647          * in order to accurately measure the text height
14648          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14649          */
14650         createInstance : function(el, fixedWidth){
14651             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14652         }
14653     };
14654 }();
14655
14656  
14657
14658 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14659     var ml = new Roo.Element(document.createElement('div'));
14660     document.body.appendChild(ml.dom);
14661     ml.position('absolute');
14662     ml.setLeftTop(-1000, -1000);
14663     ml.hide();
14664
14665     if(fixedWidth){
14666         ml.setWidth(fixedWidth);
14667     }
14668      
14669     var instance = {
14670         /**
14671          * Returns the size of the specified text based on the internal element's style and width properties
14672          * @memberOf Roo.util.TextMetrics.Instance#
14673          * @param {String} text The text to measure
14674          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14675          */
14676         getSize : function(text){
14677             ml.update(text);
14678             var s = ml.getSize();
14679             ml.update('');
14680             return s;
14681         },
14682
14683         /**
14684          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14685          * that can affect the size of the rendered text
14686          * @memberOf Roo.util.TextMetrics.Instance#
14687          * @param {String/HTMLElement} el The element, dom node or id
14688          */
14689         bind : function(el){
14690             ml.setStyle(
14691                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14692             );
14693         },
14694
14695         /**
14696          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14697          * to set a fixed width in order to accurately measure the text height.
14698          * @memberOf Roo.util.TextMetrics.Instance#
14699          * @param {Number} width The width to set on the element
14700          */
14701         setFixedWidth : function(width){
14702             ml.setWidth(width);
14703         },
14704
14705         /**
14706          * Returns the measured width of the specified text
14707          * @memberOf Roo.util.TextMetrics.Instance#
14708          * @param {String} text The text to measure
14709          * @return {Number} width The width in pixels
14710          */
14711         getWidth : function(text){
14712             ml.dom.style.width = 'auto';
14713             return this.getSize(text).width;
14714         },
14715
14716         /**
14717          * Returns the measured height of the specified text.  For multiline text, be sure to call
14718          * {@link #setFixedWidth} if necessary.
14719          * @memberOf Roo.util.TextMetrics.Instance#
14720          * @param {String} text The text to measure
14721          * @return {Number} height The height in pixels
14722          */
14723         getHeight : function(text){
14724             return this.getSize(text).height;
14725         }
14726     };
14727
14728     instance.bind(bindTo);
14729
14730     return instance;
14731 };
14732
14733 // backwards compat
14734 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14735  * Based on:
14736  * Ext JS Library 1.1.1
14737  * Copyright(c) 2006-2007, Ext JS, LLC.
14738  *
14739  * Originally Released Under LGPL - original licence link has changed is not relivant.
14740  *
14741  * Fork - LGPL
14742  * <script type="text/javascript">
14743  */
14744
14745 /**
14746  * @class Roo.state.Provider
14747  * Abstract base class for state provider implementations. This class provides methods
14748  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14749  * Provider interface.
14750  */
14751 Roo.state.Provider = function(){
14752     /**
14753      * @event statechange
14754      * Fires when a state change occurs.
14755      * @param {Provider} this This state provider
14756      * @param {String} key The state key which was changed
14757      * @param {String} value The encoded value for the state
14758      */
14759     this.addEvents({
14760         "statechange": true
14761     });
14762     this.state = {};
14763     Roo.state.Provider.superclass.constructor.call(this);
14764 };
14765 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14766     /**
14767      * Returns the current value for a key
14768      * @param {String} name The key name
14769      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14770      * @return {Mixed} The state data
14771      */
14772     get : function(name, defaultValue){
14773         return typeof this.state[name] == "undefined" ?
14774             defaultValue : this.state[name];
14775     },
14776     
14777     /**
14778      * Clears a value from the state
14779      * @param {String} name The key name
14780      */
14781     clear : function(name){
14782         delete this.state[name];
14783         this.fireEvent("statechange", this, name, null);
14784     },
14785     
14786     /**
14787      * Sets the value for a key
14788      * @param {String} name The key name
14789      * @param {Mixed} value The value to set
14790      */
14791     set : function(name, value){
14792         this.state[name] = value;
14793         this.fireEvent("statechange", this, name, value);
14794     },
14795     
14796     /**
14797      * Decodes a string previously encoded with {@link #encodeValue}.
14798      * @param {String} value The value to decode
14799      * @return {Mixed} The decoded value
14800      */
14801     decodeValue : function(cookie){
14802         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14803         var matches = re.exec(unescape(cookie));
14804         if(!matches || !matches[1]) return; // non state cookie
14805         var type = matches[1];
14806         var v = matches[2];
14807         switch(type){
14808             case "n":
14809                 return parseFloat(v);
14810             case "d":
14811                 return new Date(Date.parse(v));
14812             case "b":
14813                 return (v == "1");
14814             case "a":
14815                 var all = [];
14816                 var values = v.split("^");
14817                 for(var i = 0, len = values.length; i < len; i++){
14818                     all.push(this.decodeValue(values[i]));
14819                 }
14820                 return all;
14821            case "o":
14822                 var all = {};
14823                 var values = v.split("^");
14824                 for(var i = 0, len = values.length; i < len; i++){
14825                     var kv = values[i].split("=");
14826                     all[kv[0]] = this.decodeValue(kv[1]);
14827                 }
14828                 return all;
14829            default:
14830                 return v;
14831         }
14832     },
14833     
14834     /**
14835      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14836      * @param {Mixed} value The value to encode
14837      * @return {String} The encoded value
14838      */
14839     encodeValue : function(v){
14840         var enc;
14841         if(typeof v == "number"){
14842             enc = "n:" + v;
14843         }else if(typeof v == "boolean"){
14844             enc = "b:" + (v ? "1" : "0");
14845         }else if(v instanceof Date){
14846             enc = "d:" + v.toGMTString();
14847         }else if(v instanceof Array){
14848             var flat = "";
14849             for(var i = 0, len = v.length; i < len; i++){
14850                 flat += this.encodeValue(v[i]);
14851                 if(i != len-1) flat += "^";
14852             }
14853             enc = "a:" + flat;
14854         }else if(typeof v == "object"){
14855             var flat = "";
14856             for(var key in v){
14857                 if(typeof v[key] != "function"){
14858                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14859                 }
14860             }
14861             enc = "o:" + flat.substring(0, flat.length-1);
14862         }else{
14863             enc = "s:" + v;
14864         }
14865         return escape(enc);        
14866     }
14867 });
14868
14869 /*
14870  * Based on:
14871  * Ext JS Library 1.1.1
14872  * Copyright(c) 2006-2007, Ext JS, LLC.
14873  *
14874  * Originally Released Under LGPL - original licence link has changed is not relivant.
14875  *
14876  * Fork - LGPL
14877  * <script type="text/javascript">
14878  */
14879 /**
14880  * @class Roo.state.Manager
14881  * This is the global state manager. By default all components that are "state aware" check this class
14882  * for state information if you don't pass them a custom state provider. In order for this class
14883  * to be useful, it must be initialized with a provider when your application initializes.
14884  <pre><code>
14885 // in your initialization function
14886 init : function(){
14887    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14888    ...
14889    // supposed you have a {@link Roo.BorderLayout}
14890    var layout = new Roo.BorderLayout(...);
14891    layout.restoreState();
14892    // or a {Roo.BasicDialog}
14893    var dialog = new Roo.BasicDialog(...);
14894    dialog.restoreState();
14895  </code></pre>
14896  * @singleton
14897  */
14898 Roo.state.Manager = function(){
14899     var provider = new Roo.state.Provider();
14900     
14901     return {
14902         /**
14903          * Configures the default state provider for your application
14904          * @param {Provider} stateProvider The state provider to set
14905          */
14906         setProvider : function(stateProvider){
14907             provider = stateProvider;
14908         },
14909         
14910         /**
14911          * Returns the current value for a key
14912          * @param {String} name The key name
14913          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14914          * @return {Mixed} The state data
14915          */
14916         get : function(key, defaultValue){
14917             return provider.get(key, defaultValue);
14918         },
14919         
14920         /**
14921          * Sets the value for a key
14922          * @param {String} name The key name
14923          * @param {Mixed} value The state data
14924          */
14925          set : function(key, value){
14926             provider.set(key, value);
14927         },
14928         
14929         /**
14930          * Clears a value from the state
14931          * @param {String} name The key name
14932          */
14933         clear : function(key){
14934             provider.clear(key);
14935         },
14936         
14937         /**
14938          * Gets the currently configured state provider
14939          * @return {Provider} The state provider
14940          */
14941         getProvider : function(){
14942             return provider;
14943         }
14944     };
14945 }();
14946 /*
14947  * Based on:
14948  * Ext JS Library 1.1.1
14949  * Copyright(c) 2006-2007, Ext JS, LLC.
14950  *
14951  * Originally Released Under LGPL - original licence link has changed is not relivant.
14952  *
14953  * Fork - LGPL
14954  * <script type="text/javascript">
14955  */
14956 /**
14957  * @class Roo.state.CookieProvider
14958  * @extends Roo.state.Provider
14959  * The default Provider implementation which saves state via cookies.
14960  * <br />Usage:
14961  <pre><code>
14962    var cp = new Roo.state.CookieProvider({
14963        path: "/cgi-bin/",
14964        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14965        domain: "roojs.com"
14966    })
14967    Roo.state.Manager.setProvider(cp);
14968  </code></pre>
14969  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14970  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14971  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14972  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14973  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14974  * domain the page is running on including the 'www' like 'www.roojs.com')
14975  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14976  * @constructor
14977  * Create a new CookieProvider
14978  * @param {Object} config The configuration object
14979  */
14980 Roo.state.CookieProvider = function(config){
14981     Roo.state.CookieProvider.superclass.constructor.call(this);
14982     this.path = "/";
14983     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14984     this.domain = null;
14985     this.secure = false;
14986     Roo.apply(this, config);
14987     this.state = this.readCookies();
14988 };
14989
14990 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14991     // private
14992     set : function(name, value){
14993         if(typeof value == "undefined" || value === null){
14994             this.clear(name);
14995             return;
14996         }
14997         this.setCookie(name, value);
14998         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14999     },
15000
15001     // private
15002     clear : function(name){
15003         this.clearCookie(name);
15004         Roo.state.CookieProvider.superclass.clear.call(this, name);
15005     },
15006
15007     // private
15008     readCookies : function(){
15009         var cookies = {};
15010         var c = document.cookie + ";";
15011         var re = /\s?(.*?)=(.*?);/g;
15012         var matches;
15013         while((matches = re.exec(c)) != null){
15014             var name = matches[1];
15015             var value = matches[2];
15016             if(name && name.substring(0,3) == "ys-"){
15017                 cookies[name.substr(3)] = this.decodeValue(value);
15018             }
15019         }
15020         return cookies;
15021     },
15022
15023     // private
15024     setCookie : function(name, value){
15025         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15026            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15027            ((this.path == null) ? "" : ("; path=" + this.path)) +
15028            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15029            ((this.secure == true) ? "; secure" : "");
15030     },
15031
15032     // private
15033     clearCookie : function(name){
15034         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15035            ((this.path == null) ? "" : ("; path=" + this.path)) +
15036            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15037            ((this.secure == true) ? "; secure" : "");
15038     }
15039 });/*
15040  * Based on:
15041  * Ext JS Library 1.1.1
15042  * Copyright(c) 2006-2007, Ext JS, LLC.
15043  *
15044  * Originally Released Under LGPL - original licence link has changed is not relivant.
15045  *
15046  * Fork - LGPL
15047  * <script type="text/javascript">
15048  */
15049  
15050
15051 /**
15052  * @class Roo.ComponentMgr
15053  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15054  * @singleton
15055  */
15056 Roo.ComponentMgr = function(){
15057     var all = new Roo.util.MixedCollection();
15058
15059     return {
15060         /**
15061          * Registers a component.
15062          * @param {Roo.Component} c The component
15063          */
15064         register : function(c){
15065             all.add(c);
15066         },
15067
15068         /**
15069          * Unregisters a component.
15070          * @param {Roo.Component} c The component
15071          */
15072         unregister : function(c){
15073             all.remove(c);
15074         },
15075
15076         /**
15077          * Returns a component by id
15078          * @param {String} id The component id
15079          */
15080         get : function(id){
15081             return all.get(id);
15082         },
15083
15084         /**
15085          * Registers a function that will be called when a specified component is added to ComponentMgr
15086          * @param {String} id The component id
15087          * @param {Funtction} fn The callback function
15088          * @param {Object} scope The scope of the callback
15089          */
15090         onAvailable : function(id, fn, scope){
15091             all.on("add", function(index, o){
15092                 if(o.id == id){
15093                     fn.call(scope || o, o);
15094                     all.un("add", fn, scope);
15095                 }
15096             });
15097         }
15098     };
15099 }();/*
15100  * Based on:
15101  * Ext JS Library 1.1.1
15102  * Copyright(c) 2006-2007, Ext JS, LLC.
15103  *
15104  * Originally Released Under LGPL - original licence link has changed is not relivant.
15105  *
15106  * Fork - LGPL
15107  * <script type="text/javascript">
15108  */
15109  
15110 /**
15111  * @class Roo.Component
15112  * @extends Roo.util.Observable
15113  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15114  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15115  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15116  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15117  * All visual components (widgets) that require rendering into a layout should subclass Component.
15118  * @constructor
15119  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15120  * 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
15121  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15122  */
15123 Roo.Component = function(config){
15124     config = config || {};
15125     if(config.tagName || config.dom || typeof config == "string"){ // element object
15126         config = {el: config, id: config.id || config};
15127     }
15128     this.initialConfig = config;
15129
15130     Roo.apply(this, config);
15131     this.addEvents({
15132         /**
15133          * @event disable
15134          * Fires after the component is disabled.
15135              * @param {Roo.Component} this
15136              */
15137         disable : true,
15138         /**
15139          * @event enable
15140          * Fires after the component is enabled.
15141              * @param {Roo.Component} this
15142              */
15143         enable : true,
15144         /**
15145          * @event beforeshow
15146          * Fires before the component is shown.  Return false to stop the show.
15147              * @param {Roo.Component} this
15148              */
15149         beforeshow : true,
15150         /**
15151          * @event show
15152          * Fires after the component is shown.
15153              * @param {Roo.Component} this
15154              */
15155         show : true,
15156         /**
15157          * @event beforehide
15158          * Fires before the component is hidden. Return false to stop the hide.
15159              * @param {Roo.Component} this
15160              */
15161         beforehide : true,
15162         /**
15163          * @event hide
15164          * Fires after the component is hidden.
15165              * @param {Roo.Component} this
15166              */
15167         hide : true,
15168         /**
15169          * @event beforerender
15170          * Fires before the component is rendered. Return false to stop the render.
15171              * @param {Roo.Component} this
15172              */
15173         beforerender : true,
15174         /**
15175          * @event render
15176          * Fires after the component is rendered.
15177              * @param {Roo.Component} this
15178              */
15179         render : true,
15180         /**
15181          * @event beforedestroy
15182          * Fires before the component is destroyed. Return false to stop the destroy.
15183              * @param {Roo.Component} this
15184              */
15185         beforedestroy : true,
15186         /**
15187          * @event destroy
15188          * Fires after the component is destroyed.
15189              * @param {Roo.Component} this
15190              */
15191         destroy : true
15192     });
15193     if(!this.id){
15194         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15195     }
15196     Roo.ComponentMgr.register(this);
15197     Roo.Component.superclass.constructor.call(this);
15198     this.initComponent();
15199     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15200         this.render(this.renderTo);
15201         delete this.renderTo;
15202     }
15203 };
15204
15205 /** @private */
15206 Roo.Component.AUTO_ID = 1000;
15207
15208 Roo.extend(Roo.Component, Roo.util.Observable, {
15209     /**
15210      * @scope Roo.Component.prototype
15211      * @type {Boolean}
15212      * true if this component is hidden. Read-only.
15213      */
15214     hidden : false,
15215     /**
15216      * @type {Boolean}
15217      * true if this component is disabled. Read-only.
15218      */
15219     disabled : false,
15220     /**
15221      * @type {Boolean}
15222      * true if this component has been rendered. Read-only.
15223      */
15224     rendered : false,
15225     
15226     /** @cfg {String} disableClass
15227      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15228      */
15229     disabledClass : "x-item-disabled",
15230         /** @cfg {Boolean} allowDomMove
15231          * Whether the component can move the Dom node when rendering (defaults to true).
15232          */
15233     allowDomMove : true,
15234     /** @cfg {String} hideMode (display|visibility)
15235      * How this component should hidden. Supported values are
15236      * "visibility" (css visibility), "offsets" (negative offset position) and
15237      * "display" (css display) - defaults to "display".
15238      */
15239     hideMode: 'display',
15240
15241     /** @private */
15242     ctype : "Roo.Component",
15243
15244     /**
15245      * @cfg {String} actionMode 
15246      * which property holds the element that used for  hide() / show() / disable() / enable()
15247      * default is 'el' 
15248      */
15249     actionMode : "el",
15250
15251     /** @private */
15252     getActionEl : function(){
15253         return this[this.actionMode];
15254     },
15255
15256     initComponent : Roo.emptyFn,
15257     /**
15258      * If this is a lazy rendering component, render it to its container element.
15259      * @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.
15260      */
15261     render : function(container, position){
15262         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15263             if(!container && this.el){
15264                 this.el = Roo.get(this.el);
15265                 container = this.el.dom.parentNode;
15266                 this.allowDomMove = false;
15267             }
15268             this.container = Roo.get(container);
15269             this.rendered = true;
15270             if(position !== undefined){
15271                 if(typeof position == 'number'){
15272                     position = this.container.dom.childNodes[position];
15273                 }else{
15274                     position = Roo.getDom(position);
15275                 }
15276             }
15277             this.onRender(this.container, position || null);
15278             if(this.cls){
15279                 this.el.addClass(this.cls);
15280                 delete this.cls;
15281             }
15282             if(this.style){
15283                 this.el.applyStyles(this.style);
15284                 delete this.style;
15285             }
15286             this.fireEvent("render", this);
15287             this.afterRender(this.container);
15288             if(this.hidden){
15289                 this.hide();
15290             }
15291             if(this.disabled){
15292                 this.disable();
15293             }
15294         }
15295         return this;
15296     },
15297
15298     /** @private */
15299     // default function is not really useful
15300     onRender : function(ct, position){
15301         if(this.el){
15302             this.el = Roo.get(this.el);
15303             if(this.allowDomMove !== false){
15304                 ct.dom.insertBefore(this.el.dom, position);
15305             }
15306         }
15307     },
15308
15309     /** @private */
15310     getAutoCreate : function(){
15311         var cfg = typeof this.autoCreate == "object" ?
15312                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15313         if(this.id && !cfg.id){
15314             cfg.id = this.id;
15315         }
15316         return cfg;
15317     },
15318
15319     /** @private */
15320     afterRender : Roo.emptyFn,
15321
15322     /**
15323      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15324      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15325      */
15326     destroy : function(){
15327         if(this.fireEvent("beforedestroy", this) !== false){
15328             this.purgeListeners();
15329             this.beforeDestroy();
15330             if(this.rendered){
15331                 this.el.removeAllListeners();
15332                 this.el.remove();
15333                 if(this.actionMode == "container"){
15334                     this.container.remove();
15335                 }
15336             }
15337             this.onDestroy();
15338             Roo.ComponentMgr.unregister(this);
15339             this.fireEvent("destroy", this);
15340         }
15341     },
15342
15343         /** @private */
15344     beforeDestroy : function(){
15345
15346     },
15347
15348         /** @private */
15349         onDestroy : function(){
15350
15351     },
15352
15353     /**
15354      * Returns the underlying {@link Roo.Element}.
15355      * @return {Roo.Element} The element
15356      */
15357     getEl : function(){
15358         return this.el;
15359     },
15360
15361     /**
15362      * Returns the id of this component.
15363      * @return {String}
15364      */
15365     getId : function(){
15366         return this.id;
15367     },
15368
15369     /**
15370      * Try to focus this component.
15371      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15372      * @return {Roo.Component} this
15373      */
15374     focus : function(selectText){
15375         if(this.rendered){
15376             this.el.focus();
15377             if(selectText === true){
15378                 this.el.dom.select();
15379             }
15380         }
15381         return this;
15382     },
15383
15384     /** @private */
15385     blur : function(){
15386         if(this.rendered){
15387             this.el.blur();
15388         }
15389         return this;
15390     },
15391
15392     /**
15393      * Disable this component.
15394      * @return {Roo.Component} this
15395      */
15396     disable : function(){
15397         if(this.rendered){
15398             this.onDisable();
15399         }
15400         this.disabled = true;
15401         this.fireEvent("disable", this);
15402         return this;
15403     },
15404
15405         // private
15406     onDisable : function(){
15407         this.getActionEl().addClass(this.disabledClass);
15408         this.el.dom.disabled = true;
15409     },
15410
15411     /**
15412      * Enable this component.
15413      * @return {Roo.Component} this
15414      */
15415     enable : function(){
15416         if(this.rendered){
15417             this.onEnable();
15418         }
15419         this.disabled = false;
15420         this.fireEvent("enable", this);
15421         return this;
15422     },
15423
15424         // private
15425     onEnable : function(){
15426         this.getActionEl().removeClass(this.disabledClass);
15427         this.el.dom.disabled = false;
15428     },
15429
15430     /**
15431      * Convenience function for setting disabled/enabled by boolean.
15432      * @param {Boolean} disabled
15433      */
15434     setDisabled : function(disabled){
15435         this[disabled ? "disable" : "enable"]();
15436     },
15437
15438     /**
15439      * Show this component.
15440      * @return {Roo.Component} this
15441      */
15442     show: function(){
15443         if(this.fireEvent("beforeshow", this) !== false){
15444             this.hidden = false;
15445             if(this.rendered){
15446                 this.onShow();
15447             }
15448             this.fireEvent("show", this);
15449         }
15450         return this;
15451     },
15452
15453     // private
15454     onShow : function(){
15455         var ae = this.getActionEl();
15456         if(this.hideMode == 'visibility'){
15457             ae.dom.style.visibility = "visible";
15458         }else if(this.hideMode == 'offsets'){
15459             ae.removeClass('x-hidden');
15460         }else{
15461             ae.dom.style.display = "";
15462         }
15463     },
15464
15465     /**
15466      * Hide this component.
15467      * @return {Roo.Component} this
15468      */
15469     hide: function(){
15470         if(this.fireEvent("beforehide", this) !== false){
15471             this.hidden = true;
15472             if(this.rendered){
15473                 this.onHide();
15474             }
15475             this.fireEvent("hide", this);
15476         }
15477         return this;
15478     },
15479
15480     // private
15481     onHide : function(){
15482         var ae = this.getActionEl();
15483         if(this.hideMode == 'visibility'){
15484             ae.dom.style.visibility = "hidden";
15485         }else if(this.hideMode == 'offsets'){
15486             ae.addClass('x-hidden');
15487         }else{
15488             ae.dom.style.display = "none";
15489         }
15490     },
15491
15492     /**
15493      * Convenience function to hide or show this component by boolean.
15494      * @param {Boolean} visible True to show, false to hide
15495      * @return {Roo.Component} this
15496      */
15497     setVisible: function(visible){
15498         if(visible) {
15499             this.show();
15500         }else{
15501             this.hide();
15502         }
15503         return this;
15504     },
15505
15506     /**
15507      * Returns true if this component is visible.
15508      */
15509     isVisible : function(){
15510         return this.getActionEl().isVisible();
15511     },
15512
15513     cloneConfig : function(overrides){
15514         overrides = overrides || {};
15515         var id = overrides.id || Roo.id();
15516         var cfg = Roo.applyIf(overrides, this.initialConfig);
15517         cfg.id = id; // prevent dup id
15518         return new this.constructor(cfg);
15519     }
15520 });/*
15521  * Based on:
15522  * Ext JS Library 1.1.1
15523  * Copyright(c) 2006-2007, Ext JS, LLC.
15524  *
15525  * Originally Released Under LGPL - original licence link has changed is not relivant.
15526  *
15527  * Fork - LGPL
15528  * <script type="text/javascript">
15529  */
15530
15531 /**
15532  * @class Roo.BoxComponent
15533  * @extends Roo.Component
15534  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15535  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15536  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15537  * layout containers.
15538  * @constructor
15539  * @param {Roo.Element/String/Object} config The configuration options.
15540  */
15541 Roo.BoxComponent = function(config){
15542     Roo.Component.call(this, config);
15543     this.addEvents({
15544         /**
15545          * @event resize
15546          * Fires after the component is resized.
15547              * @param {Roo.Component} this
15548              * @param {Number} adjWidth The box-adjusted width that was set
15549              * @param {Number} adjHeight The box-adjusted height that was set
15550              * @param {Number} rawWidth The width that was originally specified
15551              * @param {Number} rawHeight The height that was originally specified
15552              */
15553         resize : true,
15554         /**
15555          * @event move
15556          * Fires after the component is moved.
15557              * @param {Roo.Component} this
15558              * @param {Number} x The new x position
15559              * @param {Number} y The new y position
15560              */
15561         move : true
15562     });
15563 };
15564
15565 Roo.extend(Roo.BoxComponent, Roo.Component, {
15566     // private, set in afterRender to signify that the component has been rendered
15567     boxReady : false,
15568     // private, used to defer height settings to subclasses
15569     deferHeight: false,
15570     /** @cfg {Number} width
15571      * width (optional) size of component
15572      */
15573      /** @cfg {Number} height
15574      * height (optional) size of component
15575      */
15576      
15577     /**
15578      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15579      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15580      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15581      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15582      * @return {Roo.BoxComponent} this
15583      */
15584     setSize : function(w, h){
15585         // support for standard size objects
15586         if(typeof w == 'object'){
15587             h = w.height;
15588             w = w.width;
15589         }
15590         // not rendered
15591         if(!this.boxReady){
15592             this.width = w;
15593             this.height = h;
15594             return this;
15595         }
15596
15597         // prevent recalcs when not needed
15598         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15599             return this;
15600         }
15601         this.lastSize = {width: w, height: h};
15602
15603         var adj = this.adjustSize(w, h);
15604         var aw = adj.width, ah = adj.height;
15605         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15606             var rz = this.getResizeEl();
15607             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15608                 rz.setSize(aw, ah);
15609             }else if(!this.deferHeight && ah !== undefined){
15610                 rz.setHeight(ah);
15611             }else if(aw !== undefined){
15612                 rz.setWidth(aw);
15613             }
15614             this.onResize(aw, ah, w, h);
15615             this.fireEvent('resize', this, aw, ah, w, h);
15616         }
15617         return this;
15618     },
15619
15620     /**
15621      * Gets the current size of the component's underlying element.
15622      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15623      */
15624     getSize : function(){
15625         return this.el.getSize();
15626     },
15627
15628     /**
15629      * Gets the current XY position of the component's underlying element.
15630      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15631      * @return {Array} The XY position of the element (e.g., [100, 200])
15632      */
15633     getPosition : function(local){
15634         if(local === true){
15635             return [this.el.getLeft(true), this.el.getTop(true)];
15636         }
15637         return this.xy || this.el.getXY();
15638     },
15639
15640     /**
15641      * Gets the current box measurements of the component's underlying element.
15642      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15643      * @returns {Object} box An object in the format {x, y, width, height}
15644      */
15645     getBox : function(local){
15646         var s = this.el.getSize();
15647         if(local){
15648             s.x = this.el.getLeft(true);
15649             s.y = this.el.getTop(true);
15650         }else{
15651             var xy = this.xy || this.el.getXY();
15652             s.x = xy[0];
15653             s.y = xy[1];
15654         }
15655         return s;
15656     },
15657
15658     /**
15659      * Sets the current box measurements of the component's underlying element.
15660      * @param {Object} box An object in the format {x, y, width, height}
15661      * @returns {Roo.BoxComponent} this
15662      */
15663     updateBox : function(box){
15664         this.setSize(box.width, box.height);
15665         this.setPagePosition(box.x, box.y);
15666         return this;
15667     },
15668
15669     // protected
15670     getResizeEl : function(){
15671         return this.resizeEl || this.el;
15672     },
15673
15674     // protected
15675     getPositionEl : function(){
15676         return this.positionEl || this.el;
15677     },
15678
15679     /**
15680      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15681      * This method fires the move event.
15682      * @param {Number} left The new left
15683      * @param {Number} top The new top
15684      * @returns {Roo.BoxComponent} this
15685      */
15686     setPosition : function(x, y){
15687         this.x = x;
15688         this.y = y;
15689         if(!this.boxReady){
15690             return this;
15691         }
15692         var adj = this.adjustPosition(x, y);
15693         var ax = adj.x, ay = adj.y;
15694
15695         var el = this.getPositionEl();
15696         if(ax !== undefined || ay !== undefined){
15697             if(ax !== undefined && ay !== undefined){
15698                 el.setLeftTop(ax, ay);
15699             }else if(ax !== undefined){
15700                 el.setLeft(ax);
15701             }else if(ay !== undefined){
15702                 el.setTop(ay);
15703             }
15704             this.onPosition(ax, ay);
15705             this.fireEvent('move', this, ax, ay);
15706         }
15707         return this;
15708     },
15709
15710     /**
15711      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15712      * This method fires the move event.
15713      * @param {Number} x The new x position
15714      * @param {Number} y The new y position
15715      * @returns {Roo.BoxComponent} this
15716      */
15717     setPagePosition : function(x, y){
15718         this.pageX = x;
15719         this.pageY = y;
15720         if(!this.boxReady){
15721             return;
15722         }
15723         if(x === undefined || y === undefined){ // cannot translate undefined points
15724             return;
15725         }
15726         var p = this.el.translatePoints(x, y);
15727         this.setPosition(p.left, p.top);
15728         return this;
15729     },
15730
15731     // private
15732     onRender : function(ct, position){
15733         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15734         if(this.resizeEl){
15735             this.resizeEl = Roo.get(this.resizeEl);
15736         }
15737         if(this.positionEl){
15738             this.positionEl = Roo.get(this.positionEl);
15739         }
15740     },
15741
15742     // private
15743     afterRender : function(){
15744         Roo.BoxComponent.superclass.afterRender.call(this);
15745         this.boxReady = true;
15746         this.setSize(this.width, this.height);
15747         if(this.x || this.y){
15748             this.setPosition(this.x, this.y);
15749         }
15750         if(this.pageX || this.pageY){
15751             this.setPagePosition(this.pageX, this.pageY);
15752         }
15753     },
15754
15755     /**
15756      * Force the component's size to recalculate based on the underlying element's current height and width.
15757      * @returns {Roo.BoxComponent} this
15758      */
15759     syncSize : function(){
15760         delete this.lastSize;
15761         this.setSize(this.el.getWidth(), this.el.getHeight());
15762         return this;
15763     },
15764
15765     /**
15766      * Called after the component is resized, this method is empty by default but can be implemented by any
15767      * subclass that needs to perform custom logic after a resize occurs.
15768      * @param {Number} adjWidth The box-adjusted width that was set
15769      * @param {Number} adjHeight The box-adjusted height that was set
15770      * @param {Number} rawWidth The width that was originally specified
15771      * @param {Number} rawHeight The height that was originally specified
15772      */
15773     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15774
15775     },
15776
15777     /**
15778      * Called after the component is moved, this method is empty by default but can be implemented by any
15779      * subclass that needs to perform custom logic after a move occurs.
15780      * @param {Number} x The new x position
15781      * @param {Number} y The new y position
15782      */
15783     onPosition : function(x, y){
15784
15785     },
15786
15787     // private
15788     adjustSize : function(w, h){
15789         if(this.autoWidth){
15790             w = 'auto';
15791         }
15792         if(this.autoHeight){
15793             h = 'auto';
15794         }
15795         return {width : w, height: h};
15796     },
15797
15798     // private
15799     adjustPosition : function(x, y){
15800         return {x : x, y: y};
15801     }
15802 });/*
15803  * Original code for Roojs - LGPL
15804  * <script type="text/javascript">
15805  */
15806  
15807 /**
15808  * @class Roo.XComponent
15809  * A delayed Element creator...
15810  * Or a way to group chunks of interface together.
15811  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15812  *  used in conjunction with XComponent.build() it will create an instance of each element,
15813  *  then call addxtype() to build the User interface.
15814  * 
15815  * Mypart.xyx = new Roo.XComponent({
15816
15817     parent : 'Mypart.xyz', // empty == document.element.!!
15818     order : '001',
15819     name : 'xxxx'
15820     region : 'xxxx'
15821     disabled : function() {} 
15822      
15823     tree : function() { // return an tree of xtype declared components
15824         var MODULE = this;
15825         return 
15826         {
15827             xtype : 'NestedLayoutPanel',
15828             // technicall
15829         }
15830      ]
15831  *})
15832  *
15833  *
15834  * It can be used to build a big heiracy, with parent etc.
15835  * or you can just use this to render a single compoent to a dom element
15836  * MYPART.render(Roo.Element | String(id) | dom_element )
15837  *
15838  *
15839  * Usage patterns.
15840  *
15841  * Classic Roo
15842  *
15843  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15844  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15845  *
15846  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15847  *
15848  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15849  * - if mulitple topModules exist, the last one is defined as the top module.
15850  *
15851  * Embeded Roo
15852  * 
15853  * When the top level or multiple modules are to embedded into a existing HTML page,
15854  * the parent element can container '#id' of the element where the module will be drawn.
15855  *
15856  * Bootstrap Roo
15857  *
15858  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15859  * it relies more on a include mechanism, where sub modules are included into an outer page.
15860  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15861  * 
15862  * Bootstrap Roo Included elements
15863  *
15864  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15865  * hence confusing the component builder as it thinks there are multiple top level elements. 
15866  *
15867  * 
15868  * 
15869  * @extends Roo.util.Observable
15870  * @constructor
15871  * @param cfg {Object} configuration of component
15872  * 
15873  */
15874 Roo.XComponent = function(cfg) {
15875     Roo.apply(this, cfg);
15876     this.addEvents({ 
15877         /**
15878              * @event built
15879              * Fires when this the componnt is built
15880              * @param {Roo.XComponent} c the component
15881              */
15882         'built' : true
15883         
15884     });
15885     this.region = this.region || 'center'; // default..
15886     Roo.XComponent.register(this);
15887     this.modules = false;
15888     this.el = false; // where the layout goes..
15889     
15890     
15891 }
15892 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15893     /**
15894      * @property el
15895      * The created element (with Roo.factory())
15896      * @type {Roo.Layout}
15897      */
15898     el  : false,
15899     
15900     /**
15901      * @property el
15902      * for BC  - use el in new code
15903      * @type {Roo.Layout}
15904      */
15905     panel : false,
15906     
15907     /**
15908      * @property layout
15909      * for BC  - use el in new code
15910      * @type {Roo.Layout}
15911      */
15912     layout : false,
15913     
15914      /**
15915      * @cfg {Function|boolean} disabled
15916      * If this module is disabled by some rule, return true from the funtion
15917      */
15918     disabled : false,
15919     
15920     /**
15921      * @cfg {String} parent 
15922      * Name of parent element which it get xtype added to..
15923      */
15924     parent: false,
15925     
15926     /**
15927      * @cfg {String} order
15928      * Used to set the order in which elements are created (usefull for multiple tabs)
15929      */
15930     
15931     order : false,
15932     /**
15933      * @cfg {String} name
15934      * String to display while loading.
15935      */
15936     name : false,
15937     /**
15938      * @cfg {String} region
15939      * Region to render component to (defaults to center)
15940      */
15941     region : 'center',
15942     
15943     /**
15944      * @cfg {Array} items
15945      * A single item array - the first element is the root of the tree..
15946      * It's done this way to stay compatible with the Xtype system...
15947      */
15948     items : false,
15949     
15950     /**
15951      * @property _tree
15952      * The method that retuns the tree of parts that make up this compoennt 
15953      * @type {function}
15954      */
15955     _tree  : false,
15956     
15957      /**
15958      * render
15959      * render element to dom or tree
15960      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15961      */
15962     
15963     render : function(el)
15964     {
15965         
15966         el = el || false;
15967         var hp = this.parent ? 1 : 0;
15968         Roo.debug &&  Roo.log(this);
15969         
15970         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15971             // if parent is a '#.....' string, then let's use that..
15972             var ename = this.parent.substr(1);
15973             this.parent = false;
15974             Roo.debug && Roo.log(ename);
15975             switch (ename) {
15976                 case 'bootstrap-body' :
15977                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15978                         this.parent = { el :  new  Roo.bootstrap.Body() };
15979                         Roo.debug && Roo.log("setting el to doc body");
15980                          
15981                     } else {
15982                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15983                     }
15984                     break;
15985                 case 'bootstrap':
15986                     this.parent = { el : true};
15987                     // fall through
15988                 default:
15989                     el = Roo.get(ename);
15990                     break;
15991             }
15992                 
15993             
15994             if (!el && !this.parent) {
15995                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15996                 return;
15997             }
15998         }
15999         Roo.debug && Roo.log("EL:");
16000         Roo.debug && Roo.log(el);
16001         Roo.debug && Roo.log("this.parent.el:");
16002         Roo.debug && Roo.log(this.parent.el);
16003         
16004         var tree = this._tree ? this._tree() : this.tree();
16005
16006         // altertive root elements ??? - we need a better way to indicate these.
16007         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16008                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16009         
16010         if (!this.parent && is_alt) {
16011             //el = Roo.get(document.body);
16012             this.parent = { el : true };
16013         }
16014             
16015             
16016         
16017         if (!this.parent) {
16018             
16019             Roo.debug && Roo.log("no parent - creating one");
16020             
16021             el = el ? Roo.get(el) : false;      
16022             
16023             // it's a top level one..
16024             this.parent =  {
16025                 el : new Roo.BorderLayout(el || document.body, {
16026                 
16027                      center: {
16028                          titlebar: false,
16029                          autoScroll:false,
16030                          closeOnTab: true,
16031                          tabPosition: 'top',
16032                           //resizeTabs: true,
16033                          alwaysShowTabs: el && hp? false :  true,
16034                          hideTabs: el || !hp ? true :  false,
16035                          minTabWidth: 140
16036                      }
16037                  })
16038             }
16039         }
16040         
16041         if (!this.parent.el) {
16042                 // probably an old style ctor, which has been disabled.
16043                 return;
16044
16045         }
16046                 // The 'tree' method is  '_tree now' 
16047             
16048         tree.region = tree.region || this.region;
16049         
16050         if (this.parent.el === true) {
16051             // bootstrap... - body..
16052             this.parent.el = Roo.factory(tree);
16053         }
16054         
16055         this.el = this.parent.el.addxtype(tree);
16056         this.fireEvent('built', this);
16057         
16058         this.panel = this.el;
16059         this.layout = this.panel.layout;
16060         this.parentLayout = this.parent.layout  || false;  
16061          
16062     }
16063     
16064 });
16065
16066 Roo.apply(Roo.XComponent, {
16067     /**
16068      * @property  hideProgress
16069      * true to disable the building progress bar.. usefull on single page renders.
16070      * @type Boolean
16071      */
16072     hideProgress : false,
16073     /**
16074      * @property  buildCompleted
16075      * True when the builder has completed building the interface.
16076      * @type Boolean
16077      */
16078     buildCompleted : false,
16079      
16080     /**
16081      * @property  topModule
16082      * the upper most module - uses document.element as it's constructor.
16083      * @type Object
16084      */
16085      
16086     topModule  : false,
16087       
16088     /**
16089      * @property  modules
16090      * array of modules to be created by registration system.
16091      * @type {Array} of Roo.XComponent
16092      */
16093     
16094     modules : [],
16095     /**
16096      * @property  elmodules
16097      * array of modules to be created by which use #ID 
16098      * @type {Array} of Roo.XComponent
16099      */
16100      
16101     elmodules : [],
16102
16103      /**
16104      * @property  build_from_html
16105      * Build elements from html - used by bootstrap HTML stuff 
16106      *    - this is cleared after build is completed
16107      * @type {boolean} true  (default false)
16108      */
16109      
16110     build_from_html : false,
16111
16112     /**
16113      * Register components to be built later.
16114      *
16115      * This solves the following issues
16116      * - Building is not done on page load, but after an authentication process has occured.
16117      * - Interface elements are registered on page load
16118      * - Parent Interface elements may not be loaded before child, so this handles that..
16119      * 
16120      *
16121      * example:
16122      * 
16123      * MyApp.register({
16124           order : '000001',
16125           module : 'Pman.Tab.projectMgr',
16126           region : 'center',
16127           parent : 'Pman.layout',
16128           disabled : false,  // or use a function..
16129         })
16130      
16131      * * @param {Object} details about module
16132      */
16133     register : function(obj) {
16134                 
16135         Roo.XComponent.event.fireEvent('register', obj);
16136         switch(typeof(obj.disabled) ) {
16137                 
16138             case 'undefined':
16139                 break;
16140             
16141             case 'function':
16142                 if ( obj.disabled() ) {
16143                         return;
16144                 }
16145                 break;
16146             
16147             default:
16148                 if (obj.disabled) {
16149                         return;
16150                 }
16151                 break;
16152         }
16153                 
16154         this.modules.push(obj);
16155          
16156     },
16157     /**
16158      * convert a string to an object..
16159      * eg. 'AAA.BBB' -> finds AAA.BBB
16160
16161      */
16162     
16163     toObject : function(str)
16164     {
16165         if (!str || typeof(str) == 'object') {
16166             return str;
16167         }
16168         if (str.substring(0,1) == '#') {
16169             return str;
16170         }
16171
16172         var ar = str.split('.');
16173         var rt, o;
16174         rt = ar.shift();
16175             /** eval:var:o */
16176         try {
16177             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16178         } catch (e) {
16179             throw "Module not found : " + str;
16180         }
16181         
16182         if (o === false) {
16183             throw "Module not found : " + str;
16184         }
16185         Roo.each(ar, function(e) {
16186             if (typeof(o[e]) == 'undefined') {
16187                 throw "Module not found : " + str;
16188             }
16189             o = o[e];
16190         });
16191         
16192         return o;
16193         
16194     },
16195     
16196     
16197     /**
16198      * move modules into their correct place in the tree..
16199      * 
16200      */
16201     preBuild : function ()
16202     {
16203         var _t = this;
16204         Roo.each(this.modules , function (obj)
16205         {
16206             Roo.XComponent.event.fireEvent('beforebuild', obj);
16207             
16208             var opar = obj.parent;
16209             try { 
16210                 obj.parent = this.toObject(opar);
16211             } catch(e) {
16212                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16213                 return;
16214             }
16215             
16216             if (!obj.parent) {
16217                 Roo.debug && Roo.log("GOT top level module");
16218                 Roo.debug && Roo.log(obj);
16219                 obj.modules = new Roo.util.MixedCollection(false, 
16220                     function(o) { return o.order + '' }
16221                 );
16222                 this.topModule = obj;
16223                 return;
16224             }
16225                         // parent is a string (usually a dom element name..)
16226             if (typeof(obj.parent) == 'string') {
16227                 this.elmodules.push(obj);
16228                 return;
16229             }
16230             if (obj.parent.constructor != Roo.XComponent) {
16231                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16232             }
16233             if (!obj.parent.modules) {
16234                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16235                     function(o) { return o.order + '' }
16236                 );
16237             }
16238             if (obj.parent.disabled) {
16239                 obj.disabled = true;
16240             }
16241             obj.parent.modules.add(obj);
16242         }, this);
16243     },
16244     
16245      /**
16246      * make a list of modules to build.
16247      * @return {Array} list of modules. 
16248      */ 
16249     
16250     buildOrder : function()
16251     {
16252         var _this = this;
16253         var cmp = function(a,b) {   
16254             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16255         };
16256         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16257             throw "No top level modules to build";
16258         }
16259         
16260         // make a flat list in order of modules to build.
16261         var mods = this.topModule ? [ this.topModule ] : [];
16262                 
16263         
16264         // elmodules (is a list of DOM based modules )
16265         Roo.each(this.elmodules, function(e) {
16266             mods.push(e);
16267             if (!this.topModule &&
16268                 typeof(e.parent) == 'string' &&
16269                 e.parent.substring(0,1) == '#' &&
16270                 Roo.get(e.parent.substr(1))
16271                ) {
16272                 
16273                 _this.topModule = e;
16274             }
16275             
16276         });
16277
16278         
16279         // add modules to their parents..
16280         var addMod = function(m) {
16281             Roo.debug && Roo.log("build Order: add: " + m.name);
16282                 
16283             mods.push(m);
16284             if (m.modules && !m.disabled) {
16285                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16286                 m.modules.keySort('ASC',  cmp );
16287                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16288     
16289                 m.modules.each(addMod);
16290             } else {
16291                 Roo.debug && Roo.log("build Order: no child modules");
16292             }
16293             // not sure if this is used any more..
16294             if (m.finalize) {
16295                 m.finalize.name = m.name + " (clean up) ";
16296                 mods.push(m.finalize);
16297             }
16298             
16299         }
16300         if (this.topModule && this.topModule.modules) { 
16301             this.topModule.modules.keySort('ASC',  cmp );
16302             this.topModule.modules.each(addMod);
16303         } 
16304         return mods;
16305     },
16306     
16307      /**
16308      * Build the registered modules.
16309      * @param {Object} parent element.
16310      * @param {Function} optional method to call after module has been added.
16311      * 
16312      */ 
16313    
16314     build : function(opts) 
16315     {
16316         
16317         if (typeof(opts) != 'undefined') {
16318             Roo.apply(this,opts);
16319         }
16320         
16321         this.preBuild();
16322         var mods = this.buildOrder();
16323       
16324         //this.allmods = mods;
16325         //Roo.debug && Roo.log(mods);
16326         //return;
16327         if (!mods.length) { // should not happen
16328             throw "NO modules!!!";
16329         }
16330         
16331         
16332         var msg = "Building Interface...";
16333         // flash it up as modal - so we store the mask!?
16334         if (!this.hideProgress && Roo.MessageBox) {
16335             Roo.MessageBox.show({ title: 'loading' });
16336             Roo.MessageBox.show({
16337                title: "Please wait...",
16338                msg: msg,
16339                width:450,
16340                progress:true,
16341                closable:false,
16342                modal: false
16343               
16344             });
16345         }
16346         var total = mods.length;
16347         
16348         var _this = this;
16349         var progressRun = function() {
16350             if (!mods.length) {
16351                 Roo.debug && Roo.log('hide?');
16352                 if (!this.hideProgress && Roo.MessageBox) {
16353                     Roo.MessageBox.hide();
16354                 }
16355                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16356                 
16357                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16358                 
16359                 // THE END...
16360                 return false;   
16361             }
16362             
16363             var m = mods.shift();
16364             
16365             
16366             Roo.debug && Roo.log(m);
16367             // not sure if this is supported any more.. - modules that are are just function
16368             if (typeof(m) == 'function') { 
16369                 m.call(this);
16370                 return progressRun.defer(10, _this);
16371             } 
16372             
16373             
16374             msg = "Building Interface " + (total  - mods.length) + 
16375                     " of " + total + 
16376                     (m.name ? (' - ' + m.name) : '');
16377                         Roo.debug && Roo.log(msg);
16378             if (!this.hideProgress &&  Roo.MessageBox) { 
16379                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16380             }
16381             
16382          
16383             // is the module disabled?
16384             var disabled = (typeof(m.disabled) == 'function') ?
16385                 m.disabled.call(m.module.disabled) : m.disabled;    
16386             
16387             
16388             if (disabled) {
16389                 return progressRun(); // we do not update the display!
16390             }
16391             
16392             // now build 
16393             
16394                         
16395                         
16396             m.render();
16397             // it's 10 on top level, and 1 on others??? why...
16398             return progressRun.defer(10, _this);
16399              
16400         }
16401         progressRun.defer(1, _this);
16402      
16403         
16404         
16405     },
16406         
16407         
16408         /**
16409          * Event Object.
16410          *
16411          *
16412          */
16413         event: false, 
16414     /**
16415          * wrapper for event.on - aliased later..  
16416          * Typically use to register a event handler for register:
16417          *
16418          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16419          *
16420          */
16421     on : false
16422    
16423     
16424     
16425 });
16426
16427 Roo.XComponent.event = new Roo.util.Observable({
16428                 events : { 
16429                         /**
16430                          * @event register
16431                          * Fires when an Component is registered,
16432                          * set the disable property on the Component to stop registration.
16433                          * @param {Roo.XComponent} c the component being registerd.
16434                          * 
16435                          */
16436                         'register' : true,
16437             /**
16438                          * @event beforebuild
16439                          * Fires before each Component is built
16440                          * can be used to apply permissions.
16441                          * @param {Roo.XComponent} c the component being registerd.
16442                          * 
16443                          */
16444                         'beforebuild' : true,
16445                         /**
16446                          * @event buildcomplete
16447                          * Fires on the top level element when all elements have been built
16448                          * @param {Roo.XComponent} the top level component.
16449                          */
16450                         'buildcomplete' : true
16451                         
16452                 }
16453 });
16454
16455 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16456  /*
16457  * Based on:
16458  * Ext JS Library 1.1.1
16459  * Copyright(c) 2006-2007, Ext JS, LLC.
16460  *
16461  * Originally Released Under LGPL - original licence link has changed is not relivant.
16462  *
16463  * Fork - LGPL
16464  * <script type="text/javascript">
16465  */
16466
16467
16468
16469 /*
16470  * These classes are derivatives of the similarly named classes in the YUI Library.
16471  * The original license:
16472  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16473  * Code licensed under the BSD License:
16474  * http://developer.yahoo.net/yui/license.txt
16475  */
16476
16477 (function() {
16478
16479 var Event=Roo.EventManager;
16480 var Dom=Roo.lib.Dom;
16481
16482 /**
16483  * @class Roo.dd.DragDrop
16484  * @extends Roo.util.Observable
16485  * Defines the interface and base operation of items that that can be
16486  * dragged or can be drop targets.  It was designed to be extended, overriding
16487  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16488  * Up to three html elements can be associated with a DragDrop instance:
16489  * <ul>
16490  * <li>linked element: the element that is passed into the constructor.
16491  * This is the element which defines the boundaries for interaction with
16492  * other DragDrop objects.</li>
16493  * <li>handle element(s): The drag operation only occurs if the element that
16494  * was clicked matches a handle element.  By default this is the linked
16495  * element, but there are times that you will want only a portion of the
16496  * linked element to initiate the drag operation, and the setHandleElId()
16497  * method provides a way to define this.</li>
16498  * <li>drag element: this represents the element that would be moved along
16499  * with the cursor during a drag operation.  By default, this is the linked
16500  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16501  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16502  * </li>
16503  * </ul>
16504  * This class should not be instantiated until the onload event to ensure that
16505  * the associated elements are available.
16506  * The following would define a DragDrop obj that would interact with any
16507  * other DragDrop obj in the "group1" group:
16508  * <pre>
16509  *  dd = new Roo.dd.DragDrop("div1", "group1");
16510  * </pre>
16511  * Since none of the event handlers have been implemented, nothing would
16512  * actually happen if you were to run the code above.  Normally you would
16513  * override this class or one of the default implementations, but you can
16514  * also override the methods you want on an instance of the class...
16515  * <pre>
16516  *  dd.onDragDrop = function(e, id) {
16517  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16518  *  }
16519  * </pre>
16520  * @constructor
16521  * @param {String} id of the element that is linked to this instance
16522  * @param {String} sGroup the group of related DragDrop objects
16523  * @param {object} config an object containing configurable attributes
16524  *                Valid properties for DragDrop:
16525  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16526  */
16527 Roo.dd.DragDrop = function(id, sGroup, config) {
16528     if (id) {
16529         this.init(id, sGroup, config);
16530     }
16531     
16532 };
16533
16534 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16535
16536     /**
16537      * The id of the element associated with this object.  This is what we
16538      * refer to as the "linked element" because the size and position of
16539      * this element is used to determine when the drag and drop objects have
16540      * interacted.
16541      * @property id
16542      * @type String
16543      */
16544     id: null,
16545
16546     /**
16547      * Configuration attributes passed into the constructor
16548      * @property config
16549      * @type object
16550      */
16551     config: null,
16552
16553     /**
16554      * The id of the element that will be dragged.  By default this is same
16555      * as the linked element , but could be changed to another element. Ex:
16556      * Roo.dd.DDProxy
16557      * @property dragElId
16558      * @type String
16559      * @private
16560      */
16561     dragElId: null,
16562
16563     /**
16564      * the id of the element that initiates the drag operation.  By default
16565      * this is the linked element, but could be changed to be a child of this
16566      * element.  This lets us do things like only starting the drag when the
16567      * header element within the linked html element is clicked.
16568      * @property handleElId
16569      * @type String
16570      * @private
16571      */
16572     handleElId: null,
16573
16574     /**
16575      * An associative array of HTML tags that will be ignored if clicked.
16576      * @property invalidHandleTypes
16577      * @type {string: string}
16578      */
16579     invalidHandleTypes: null,
16580
16581     /**
16582      * An associative array of ids for elements that will be ignored if clicked
16583      * @property invalidHandleIds
16584      * @type {string: string}
16585      */
16586     invalidHandleIds: null,
16587
16588     /**
16589      * An indexted array of css class names for elements that will be ignored
16590      * if clicked.
16591      * @property invalidHandleClasses
16592      * @type string[]
16593      */
16594     invalidHandleClasses: null,
16595
16596     /**
16597      * The linked element's absolute X position at the time the drag was
16598      * started
16599      * @property startPageX
16600      * @type int
16601      * @private
16602      */
16603     startPageX: 0,
16604
16605     /**
16606      * The linked element's absolute X position at the time the drag was
16607      * started
16608      * @property startPageY
16609      * @type int
16610      * @private
16611      */
16612     startPageY: 0,
16613
16614     /**
16615      * The group defines a logical collection of DragDrop objects that are
16616      * related.  Instances only get events when interacting with other
16617      * DragDrop object in the same group.  This lets us define multiple
16618      * groups using a single DragDrop subclass if we want.
16619      * @property groups
16620      * @type {string: string}
16621      */
16622     groups: null,
16623
16624     /**
16625      * Individual drag/drop instances can be locked.  This will prevent
16626      * onmousedown start drag.
16627      * @property locked
16628      * @type boolean
16629      * @private
16630      */
16631     locked: false,
16632
16633     /**
16634      * Lock this instance
16635      * @method lock
16636      */
16637     lock: function() { this.locked = true; },
16638
16639     /**
16640      * Unlock this instace
16641      * @method unlock
16642      */
16643     unlock: function() { this.locked = false; },
16644
16645     /**
16646      * By default, all insances can be a drop target.  This can be disabled by
16647      * setting isTarget to false.
16648      * @method isTarget
16649      * @type boolean
16650      */
16651     isTarget: true,
16652
16653     /**
16654      * The padding configured for this drag and drop object for calculating
16655      * the drop zone intersection with this object.
16656      * @method padding
16657      * @type int[]
16658      */
16659     padding: null,
16660
16661     /**
16662      * Cached reference to the linked element
16663      * @property _domRef
16664      * @private
16665      */
16666     _domRef: null,
16667
16668     /**
16669      * Internal typeof flag
16670      * @property __ygDragDrop
16671      * @private
16672      */
16673     __ygDragDrop: true,
16674
16675     /**
16676      * Set to true when horizontal contraints are applied
16677      * @property constrainX
16678      * @type boolean
16679      * @private
16680      */
16681     constrainX: false,
16682
16683     /**
16684      * Set to true when vertical contraints are applied
16685      * @property constrainY
16686      * @type boolean
16687      * @private
16688      */
16689     constrainY: false,
16690
16691     /**
16692      * The left constraint
16693      * @property minX
16694      * @type int
16695      * @private
16696      */
16697     minX: 0,
16698
16699     /**
16700      * The right constraint
16701      * @property maxX
16702      * @type int
16703      * @private
16704      */
16705     maxX: 0,
16706
16707     /**
16708      * The up constraint
16709      * @property minY
16710      * @type int
16711      * @type int
16712      * @private
16713      */
16714     minY: 0,
16715
16716     /**
16717      * The down constraint
16718      * @property maxY
16719      * @type int
16720      * @private
16721      */
16722     maxY: 0,
16723
16724     /**
16725      * Maintain offsets when we resetconstraints.  Set to true when you want
16726      * the position of the element relative to its parent to stay the same
16727      * when the page changes
16728      *
16729      * @property maintainOffset
16730      * @type boolean
16731      */
16732     maintainOffset: false,
16733
16734     /**
16735      * Array of pixel locations the element will snap to if we specified a
16736      * horizontal graduation/interval.  This array is generated automatically
16737      * when you define a tick interval.
16738      * @property xTicks
16739      * @type int[]
16740      */
16741     xTicks: null,
16742
16743     /**
16744      * Array of pixel locations the element will snap to if we specified a
16745      * vertical graduation/interval.  This array is generated automatically
16746      * when you define a tick interval.
16747      * @property yTicks
16748      * @type int[]
16749      */
16750     yTicks: null,
16751
16752     /**
16753      * By default the drag and drop instance will only respond to the primary
16754      * button click (left button for a right-handed mouse).  Set to true to
16755      * allow drag and drop to start with any mouse click that is propogated
16756      * by the browser
16757      * @property primaryButtonOnly
16758      * @type boolean
16759      */
16760     primaryButtonOnly: true,
16761
16762     /**
16763      * The availabe property is false until the linked dom element is accessible.
16764      * @property available
16765      * @type boolean
16766      */
16767     available: false,
16768
16769     /**
16770      * By default, drags can only be initiated if the mousedown occurs in the
16771      * region the linked element is.  This is done in part to work around a
16772      * bug in some browsers that mis-report the mousedown if the previous
16773      * mouseup happened outside of the window.  This property is set to true
16774      * if outer handles are defined.
16775      *
16776      * @property hasOuterHandles
16777      * @type boolean
16778      * @default false
16779      */
16780     hasOuterHandles: false,
16781
16782     /**
16783      * Code that executes immediately before the startDrag event
16784      * @method b4StartDrag
16785      * @private
16786      */
16787     b4StartDrag: function(x, y) { },
16788
16789     /**
16790      * Abstract method called after a drag/drop object is clicked
16791      * and the drag or mousedown time thresholds have beeen met.
16792      * @method startDrag
16793      * @param {int} X click location
16794      * @param {int} Y click location
16795      */
16796     startDrag: function(x, y) { /* override this */ },
16797
16798     /**
16799      * Code that executes immediately before the onDrag event
16800      * @method b4Drag
16801      * @private
16802      */
16803     b4Drag: function(e) { },
16804
16805     /**
16806      * Abstract method called during the onMouseMove event while dragging an
16807      * object.
16808      * @method onDrag
16809      * @param {Event} e the mousemove event
16810      */
16811     onDrag: function(e) { /* override this */ },
16812
16813     /**
16814      * Abstract method called when this element fist begins hovering over
16815      * another DragDrop obj
16816      * @method onDragEnter
16817      * @param {Event} e the mousemove event
16818      * @param {String|DragDrop[]} id In POINT mode, the element
16819      * id this is hovering over.  In INTERSECT mode, an array of one or more
16820      * dragdrop items being hovered over.
16821      */
16822     onDragEnter: function(e, id) { /* override this */ },
16823
16824     /**
16825      * Code that executes immediately before the onDragOver event
16826      * @method b4DragOver
16827      * @private
16828      */
16829     b4DragOver: function(e) { },
16830
16831     /**
16832      * Abstract method called when this element is hovering over another
16833      * DragDrop obj
16834      * @method onDragOver
16835      * @param {Event} e the mousemove event
16836      * @param {String|DragDrop[]} id In POINT mode, the element
16837      * id this is hovering over.  In INTERSECT mode, an array of dd items
16838      * being hovered over.
16839      */
16840     onDragOver: function(e, id) { /* override this */ },
16841
16842     /**
16843      * Code that executes immediately before the onDragOut event
16844      * @method b4DragOut
16845      * @private
16846      */
16847     b4DragOut: function(e) { },
16848
16849     /**
16850      * Abstract method called when we are no longer hovering over an element
16851      * @method onDragOut
16852      * @param {Event} e the mousemove event
16853      * @param {String|DragDrop[]} id In POINT mode, the element
16854      * id this was hovering over.  In INTERSECT mode, an array of dd items
16855      * that the mouse is no longer over.
16856      */
16857     onDragOut: function(e, id) { /* override this */ },
16858
16859     /**
16860      * Code that executes immediately before the onDragDrop event
16861      * @method b4DragDrop
16862      * @private
16863      */
16864     b4DragDrop: function(e) { },
16865
16866     /**
16867      * Abstract method called when this item is dropped on another DragDrop
16868      * obj
16869      * @method onDragDrop
16870      * @param {Event} e the mouseup event
16871      * @param {String|DragDrop[]} id In POINT mode, the element
16872      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16873      * was dropped on.
16874      */
16875     onDragDrop: function(e, id) { /* override this */ },
16876
16877     /**
16878      * Abstract method called when this item is dropped on an area with no
16879      * drop target
16880      * @method onInvalidDrop
16881      * @param {Event} e the mouseup event
16882      */
16883     onInvalidDrop: function(e) { /* override this */ },
16884
16885     /**
16886      * Code that executes immediately before the endDrag event
16887      * @method b4EndDrag
16888      * @private
16889      */
16890     b4EndDrag: function(e) { },
16891
16892     /**
16893      * Fired when we are done dragging the object
16894      * @method endDrag
16895      * @param {Event} e the mouseup event
16896      */
16897     endDrag: function(e) { /* override this */ },
16898
16899     /**
16900      * Code executed immediately before the onMouseDown event
16901      * @method b4MouseDown
16902      * @param {Event} e the mousedown event
16903      * @private
16904      */
16905     b4MouseDown: function(e) {  },
16906
16907     /**
16908      * Event handler that fires when a drag/drop obj gets a mousedown
16909      * @method onMouseDown
16910      * @param {Event} e the mousedown event
16911      */
16912     onMouseDown: function(e) { /* override this */ },
16913
16914     /**
16915      * Event handler that fires when a drag/drop obj gets a mouseup
16916      * @method onMouseUp
16917      * @param {Event} e the mouseup event
16918      */
16919     onMouseUp: function(e) { /* override this */ },
16920
16921     /**
16922      * Override the onAvailable method to do what is needed after the initial
16923      * position was determined.
16924      * @method onAvailable
16925      */
16926     onAvailable: function () {
16927     },
16928
16929     /*
16930      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16931      * @type Object
16932      */
16933     defaultPadding : {left:0, right:0, top:0, bottom:0},
16934
16935     /*
16936      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16937  *
16938  * Usage:
16939  <pre><code>
16940  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16941                 { dragElId: "existingProxyDiv" });
16942  dd.startDrag = function(){
16943      this.constrainTo("parent-id");
16944  };
16945  </code></pre>
16946  * Or you can initalize it using the {@link Roo.Element} object:
16947  <pre><code>
16948  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16949      startDrag : function(){
16950          this.constrainTo("parent-id");
16951      }
16952  });
16953  </code></pre>
16954      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16955      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16956      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16957      * an object containing the sides to pad. For example: {right:10, bottom:10}
16958      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16959      */
16960     constrainTo : function(constrainTo, pad, inContent){
16961         if(typeof pad == "number"){
16962             pad = {left: pad, right:pad, top:pad, bottom:pad};
16963         }
16964         pad = pad || this.defaultPadding;
16965         var b = Roo.get(this.getEl()).getBox();
16966         var ce = Roo.get(constrainTo);
16967         var s = ce.getScroll();
16968         var c, cd = ce.dom;
16969         if(cd == document.body){
16970             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16971         }else{
16972             xy = ce.getXY();
16973             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16974         }
16975
16976
16977         var topSpace = b.y - c.y;
16978         var leftSpace = b.x - c.x;
16979
16980         this.resetConstraints();
16981         this.setXConstraint(leftSpace - (pad.left||0), // left
16982                 c.width - leftSpace - b.width - (pad.right||0) //right
16983         );
16984         this.setYConstraint(topSpace - (pad.top||0), //top
16985                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16986         );
16987     },
16988
16989     /**
16990      * Returns a reference to the linked element
16991      * @method getEl
16992      * @return {HTMLElement} the html element
16993      */
16994     getEl: function() {
16995         if (!this._domRef) {
16996             this._domRef = Roo.getDom(this.id);
16997         }
16998
16999         return this._domRef;
17000     },
17001
17002     /**
17003      * Returns a reference to the actual element to drag.  By default this is
17004      * the same as the html element, but it can be assigned to another
17005      * element. An example of this can be found in Roo.dd.DDProxy
17006      * @method getDragEl
17007      * @return {HTMLElement} the html element
17008      */
17009     getDragEl: function() {
17010         return Roo.getDom(this.dragElId);
17011     },
17012
17013     /**
17014      * Sets up the DragDrop object.  Must be called in the constructor of any
17015      * Roo.dd.DragDrop subclass
17016      * @method init
17017      * @param id the id of the linked element
17018      * @param {String} sGroup the group of related items
17019      * @param {object} config configuration attributes
17020      */
17021     init: function(id, sGroup, config) {
17022         this.initTarget(id, sGroup, config);
17023         if (!Roo.isTouch) {
17024             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17025         }
17026         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17027         // Event.on(this.id, "selectstart", Event.preventDefault);
17028     },
17029
17030     /**
17031      * Initializes Targeting functionality only... the object does not
17032      * get a mousedown handler.
17033      * @method initTarget
17034      * @param id the id of the linked element
17035      * @param {String} sGroup the group of related items
17036      * @param {object} config configuration attributes
17037      */
17038     initTarget: function(id, sGroup, config) {
17039
17040         // configuration attributes
17041         this.config = config || {};
17042
17043         // create a local reference to the drag and drop manager
17044         this.DDM = Roo.dd.DDM;
17045         // initialize the groups array
17046         this.groups = {};
17047
17048         // assume that we have an element reference instead of an id if the
17049         // parameter is not a string
17050         if (typeof id !== "string") {
17051             id = Roo.id(id);
17052         }
17053
17054         // set the id
17055         this.id = id;
17056
17057         // add to an interaction group
17058         this.addToGroup((sGroup) ? sGroup : "default");
17059
17060         // We don't want to register this as the handle with the manager
17061         // so we just set the id rather than calling the setter.
17062         this.handleElId = id;
17063
17064         // the linked element is the element that gets dragged by default
17065         this.setDragElId(id);
17066
17067         // by default, clicked anchors will not start drag operations.
17068         this.invalidHandleTypes = { A: "A" };
17069         this.invalidHandleIds = {};
17070         this.invalidHandleClasses = [];
17071
17072         this.applyConfig();
17073
17074         this.handleOnAvailable();
17075     },
17076
17077     /**
17078      * Applies the configuration parameters that were passed into the constructor.
17079      * This is supposed to happen at each level through the inheritance chain.  So
17080      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17081      * DragDrop in order to get all of the parameters that are available in
17082      * each object.
17083      * @method applyConfig
17084      */
17085     applyConfig: function() {
17086
17087         // configurable properties:
17088         //    padding, isTarget, maintainOffset, primaryButtonOnly
17089         this.padding           = this.config.padding || [0, 0, 0, 0];
17090         this.isTarget          = (this.config.isTarget !== false);
17091         this.maintainOffset    = (this.config.maintainOffset);
17092         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17093
17094     },
17095
17096     /**
17097      * Executed when the linked element is available
17098      * @method handleOnAvailable
17099      * @private
17100      */
17101     handleOnAvailable: function() {
17102         this.available = true;
17103         this.resetConstraints();
17104         this.onAvailable();
17105     },
17106
17107      /**
17108      * Configures the padding for the target zone in px.  Effectively expands
17109      * (or reduces) the virtual object size for targeting calculations.
17110      * Supports css-style shorthand; if only one parameter is passed, all sides
17111      * will have that padding, and if only two are passed, the top and bottom
17112      * will have the first param, the left and right the second.
17113      * @method setPadding
17114      * @param {int} iTop    Top pad
17115      * @param {int} iRight  Right pad
17116      * @param {int} iBot    Bot pad
17117      * @param {int} iLeft   Left pad
17118      */
17119     setPadding: function(iTop, iRight, iBot, iLeft) {
17120         // this.padding = [iLeft, iRight, iTop, iBot];
17121         if (!iRight && 0 !== iRight) {
17122             this.padding = [iTop, iTop, iTop, iTop];
17123         } else if (!iBot && 0 !== iBot) {
17124             this.padding = [iTop, iRight, iTop, iRight];
17125         } else {
17126             this.padding = [iTop, iRight, iBot, iLeft];
17127         }
17128     },
17129
17130     /**
17131      * Stores the initial placement of the linked element.
17132      * @method setInitialPosition
17133      * @param {int} diffX   the X offset, default 0
17134      * @param {int} diffY   the Y offset, default 0
17135      */
17136     setInitPosition: function(diffX, diffY) {
17137         var el = this.getEl();
17138
17139         if (!this.DDM.verifyEl(el)) {
17140             return;
17141         }
17142
17143         var dx = diffX || 0;
17144         var dy = diffY || 0;
17145
17146         var p = Dom.getXY( el );
17147
17148         this.initPageX = p[0] - dx;
17149         this.initPageY = p[1] - dy;
17150
17151         this.lastPageX = p[0];
17152         this.lastPageY = p[1];
17153
17154
17155         this.setStartPosition(p);
17156     },
17157
17158     /**
17159      * Sets the start position of the element.  This is set when the obj
17160      * is initialized, the reset when a drag is started.
17161      * @method setStartPosition
17162      * @param pos current position (from previous lookup)
17163      * @private
17164      */
17165     setStartPosition: function(pos) {
17166         var p = pos || Dom.getXY( this.getEl() );
17167         this.deltaSetXY = null;
17168
17169         this.startPageX = p[0];
17170         this.startPageY = p[1];
17171     },
17172
17173     /**
17174      * Add this instance to a group of related drag/drop objects.  All
17175      * instances belong to at least one group, and can belong to as many
17176      * groups as needed.
17177      * @method addToGroup
17178      * @param sGroup {string} the name of the group
17179      */
17180     addToGroup: function(sGroup) {
17181         this.groups[sGroup] = true;
17182         this.DDM.regDragDrop(this, sGroup);
17183     },
17184
17185     /**
17186      * Remove's this instance from the supplied interaction group
17187      * @method removeFromGroup
17188      * @param {string}  sGroup  The group to drop
17189      */
17190     removeFromGroup: function(sGroup) {
17191         if (this.groups[sGroup]) {
17192             delete this.groups[sGroup];
17193         }
17194
17195         this.DDM.removeDDFromGroup(this, sGroup);
17196     },
17197
17198     /**
17199      * Allows you to specify that an element other than the linked element
17200      * will be moved with the cursor during a drag
17201      * @method setDragElId
17202      * @param id {string} the id of the element that will be used to initiate the drag
17203      */
17204     setDragElId: function(id) {
17205         this.dragElId = id;
17206     },
17207
17208     /**
17209      * Allows you to specify a child of the linked element that should be
17210      * used to initiate the drag operation.  An example of this would be if
17211      * you have a content div with text and links.  Clicking anywhere in the
17212      * content area would normally start the drag operation.  Use this method
17213      * to specify that an element inside of the content div is the element
17214      * that starts the drag operation.
17215      * @method setHandleElId
17216      * @param id {string} the id of the element that will be used to
17217      * initiate the drag.
17218      */
17219     setHandleElId: function(id) {
17220         if (typeof id !== "string") {
17221             id = Roo.id(id);
17222         }
17223         this.handleElId = id;
17224         this.DDM.regHandle(this.id, id);
17225     },
17226
17227     /**
17228      * Allows you to set an element outside of the linked element as a drag
17229      * handle
17230      * @method setOuterHandleElId
17231      * @param id the id of the element that will be used to initiate the drag
17232      */
17233     setOuterHandleElId: function(id) {
17234         if (typeof id !== "string") {
17235             id = Roo.id(id);
17236         }
17237         Event.on(id, "mousedown",
17238                 this.handleMouseDown, this);
17239         this.setHandleElId(id);
17240
17241         this.hasOuterHandles = true;
17242     },
17243
17244     /**
17245      * Remove all drag and drop hooks for this element
17246      * @method unreg
17247      */
17248     unreg: function() {
17249         Event.un(this.id, "mousedown",
17250                 this.handleMouseDown);
17251         Event.un(this.id, "touchstart",
17252                 this.handleMouseDown);
17253         this._domRef = null;
17254         this.DDM._remove(this);
17255     },
17256
17257     destroy : function(){
17258         this.unreg();
17259     },
17260
17261     /**
17262      * Returns true if this instance is locked, or the drag drop mgr is locked
17263      * (meaning that all drag/drop is disabled on the page.)
17264      * @method isLocked
17265      * @return {boolean} true if this obj or all drag/drop is locked, else
17266      * false
17267      */
17268     isLocked: function() {
17269         return (this.DDM.isLocked() || this.locked);
17270     },
17271
17272     /**
17273      * Fired when this object is clicked
17274      * @method handleMouseDown
17275      * @param {Event} e
17276      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17277      * @private
17278      */
17279     handleMouseDown: function(e, oDD){
17280      
17281         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17282             //Roo.log('not touch/ button !=0');
17283             return;
17284         }
17285         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17286             return; // double touch..
17287         }
17288         
17289
17290         if (this.isLocked()) {
17291             //Roo.log('locked');
17292             return;
17293         }
17294
17295         this.DDM.refreshCache(this.groups);
17296 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17297         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17298         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17299             //Roo.log('no outer handes or not over target');
17300                 // do nothing.
17301         } else {
17302 //            Roo.log('check validator');
17303             if (this.clickValidator(e)) {
17304 //                Roo.log('validate success');
17305                 // set the initial element position
17306                 this.setStartPosition();
17307
17308
17309                 this.b4MouseDown(e);
17310                 this.onMouseDown(e);
17311
17312                 this.DDM.handleMouseDown(e, this);
17313
17314                 this.DDM.stopEvent(e);
17315             } else {
17316
17317
17318             }
17319         }
17320     },
17321
17322     clickValidator: function(e) {
17323         var target = e.getTarget();
17324         return ( this.isValidHandleChild(target) &&
17325                     (this.id == this.handleElId ||
17326                         this.DDM.handleWasClicked(target, this.id)) );
17327     },
17328
17329     /**
17330      * Allows you to specify a tag name that should not start a drag operation
17331      * when clicked.  This is designed to facilitate embedding links within a
17332      * drag handle that do something other than start the drag.
17333      * @method addInvalidHandleType
17334      * @param {string} tagName the type of element to exclude
17335      */
17336     addInvalidHandleType: function(tagName) {
17337         var type = tagName.toUpperCase();
17338         this.invalidHandleTypes[type] = type;
17339     },
17340
17341     /**
17342      * Lets you to specify an element id for a child of a drag handle
17343      * that should not initiate a drag
17344      * @method addInvalidHandleId
17345      * @param {string} id the element id of the element you wish to ignore
17346      */
17347     addInvalidHandleId: function(id) {
17348         if (typeof id !== "string") {
17349             id = Roo.id(id);
17350         }
17351         this.invalidHandleIds[id] = id;
17352     },
17353
17354     /**
17355      * Lets you specify a css class of elements that will not initiate a drag
17356      * @method addInvalidHandleClass
17357      * @param {string} cssClass the class of the elements you wish to ignore
17358      */
17359     addInvalidHandleClass: function(cssClass) {
17360         this.invalidHandleClasses.push(cssClass);
17361     },
17362
17363     /**
17364      * Unsets an excluded tag name set by addInvalidHandleType
17365      * @method removeInvalidHandleType
17366      * @param {string} tagName the type of element to unexclude
17367      */
17368     removeInvalidHandleType: function(tagName) {
17369         var type = tagName.toUpperCase();
17370         // this.invalidHandleTypes[type] = null;
17371         delete this.invalidHandleTypes[type];
17372     },
17373
17374     /**
17375      * Unsets an invalid handle id
17376      * @method removeInvalidHandleId
17377      * @param {string} id the id of the element to re-enable
17378      */
17379     removeInvalidHandleId: function(id) {
17380         if (typeof id !== "string") {
17381             id = Roo.id(id);
17382         }
17383         delete this.invalidHandleIds[id];
17384     },
17385
17386     /**
17387      * Unsets an invalid css class
17388      * @method removeInvalidHandleClass
17389      * @param {string} cssClass the class of the element(s) you wish to
17390      * re-enable
17391      */
17392     removeInvalidHandleClass: function(cssClass) {
17393         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17394             if (this.invalidHandleClasses[i] == cssClass) {
17395                 delete this.invalidHandleClasses[i];
17396             }
17397         }
17398     },
17399
17400     /**
17401      * Checks the tag exclusion list to see if this click should be ignored
17402      * @method isValidHandleChild
17403      * @param {HTMLElement} node the HTMLElement to evaluate
17404      * @return {boolean} true if this is a valid tag type, false if not
17405      */
17406     isValidHandleChild: function(node) {
17407
17408         var valid = true;
17409         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17410         var nodeName;
17411         try {
17412             nodeName = node.nodeName.toUpperCase();
17413         } catch(e) {
17414             nodeName = node.nodeName;
17415         }
17416         valid = valid && !this.invalidHandleTypes[nodeName];
17417         valid = valid && !this.invalidHandleIds[node.id];
17418
17419         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17420             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17421         }
17422
17423
17424         return valid;
17425
17426     },
17427
17428     /**
17429      * Create the array of horizontal tick marks if an interval was specified
17430      * in setXConstraint().
17431      * @method setXTicks
17432      * @private
17433      */
17434     setXTicks: function(iStartX, iTickSize) {
17435         this.xTicks = [];
17436         this.xTickSize = iTickSize;
17437
17438         var tickMap = {};
17439
17440         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17441             if (!tickMap[i]) {
17442                 this.xTicks[this.xTicks.length] = i;
17443                 tickMap[i] = true;
17444             }
17445         }
17446
17447         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17448             if (!tickMap[i]) {
17449                 this.xTicks[this.xTicks.length] = i;
17450                 tickMap[i] = true;
17451             }
17452         }
17453
17454         this.xTicks.sort(this.DDM.numericSort) ;
17455     },
17456
17457     /**
17458      * Create the array of vertical tick marks if an interval was specified in
17459      * setYConstraint().
17460      * @method setYTicks
17461      * @private
17462      */
17463     setYTicks: function(iStartY, iTickSize) {
17464         this.yTicks = [];
17465         this.yTickSize = iTickSize;
17466
17467         var tickMap = {};
17468
17469         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17470             if (!tickMap[i]) {
17471                 this.yTicks[this.yTicks.length] = i;
17472                 tickMap[i] = true;
17473             }
17474         }
17475
17476         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17477             if (!tickMap[i]) {
17478                 this.yTicks[this.yTicks.length] = i;
17479                 tickMap[i] = true;
17480             }
17481         }
17482
17483         this.yTicks.sort(this.DDM.numericSort) ;
17484     },
17485
17486     /**
17487      * By default, the element can be dragged any place on the screen.  Use
17488      * this method to limit the horizontal travel of the element.  Pass in
17489      * 0,0 for the parameters if you want to lock the drag to the y axis.
17490      * @method setXConstraint
17491      * @param {int} iLeft the number of pixels the element can move to the left
17492      * @param {int} iRight the number of pixels the element can move to the
17493      * right
17494      * @param {int} iTickSize optional parameter for specifying that the
17495      * element
17496      * should move iTickSize pixels at a time.
17497      */
17498     setXConstraint: function(iLeft, iRight, iTickSize) {
17499         this.leftConstraint = iLeft;
17500         this.rightConstraint = iRight;
17501
17502         this.minX = this.initPageX - iLeft;
17503         this.maxX = this.initPageX + iRight;
17504         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17505
17506         this.constrainX = true;
17507     },
17508
17509     /**
17510      * Clears any constraints applied to this instance.  Also clears ticks
17511      * since they can't exist independent of a constraint at this time.
17512      * @method clearConstraints
17513      */
17514     clearConstraints: function() {
17515         this.constrainX = false;
17516         this.constrainY = false;
17517         this.clearTicks();
17518     },
17519
17520     /**
17521      * Clears any tick interval defined for this instance
17522      * @method clearTicks
17523      */
17524     clearTicks: function() {
17525         this.xTicks = null;
17526         this.yTicks = null;
17527         this.xTickSize = 0;
17528         this.yTickSize = 0;
17529     },
17530
17531     /**
17532      * By default, the element can be dragged any place on the screen.  Set
17533      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17534      * parameters if you want to lock the drag to the x axis.
17535      * @method setYConstraint
17536      * @param {int} iUp the number of pixels the element can move up
17537      * @param {int} iDown the number of pixels the element can move down
17538      * @param {int} iTickSize optional parameter for specifying that the
17539      * element should move iTickSize pixels at a time.
17540      */
17541     setYConstraint: function(iUp, iDown, iTickSize) {
17542         this.topConstraint = iUp;
17543         this.bottomConstraint = iDown;
17544
17545         this.minY = this.initPageY - iUp;
17546         this.maxY = this.initPageY + iDown;
17547         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17548
17549         this.constrainY = true;
17550
17551     },
17552
17553     /**
17554      * resetConstraints must be called if you manually reposition a dd element.
17555      * @method resetConstraints
17556      * @param {boolean} maintainOffset
17557      */
17558     resetConstraints: function() {
17559
17560
17561         // Maintain offsets if necessary
17562         if (this.initPageX || this.initPageX === 0) {
17563             // figure out how much this thing has moved
17564             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17565             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17566
17567             this.setInitPosition(dx, dy);
17568
17569         // This is the first time we have detected the element's position
17570         } else {
17571             this.setInitPosition();
17572         }
17573
17574         if (this.constrainX) {
17575             this.setXConstraint( this.leftConstraint,
17576                                  this.rightConstraint,
17577                                  this.xTickSize        );
17578         }
17579
17580         if (this.constrainY) {
17581             this.setYConstraint( this.topConstraint,
17582                                  this.bottomConstraint,
17583                                  this.yTickSize         );
17584         }
17585     },
17586
17587     /**
17588      * Normally the drag element is moved pixel by pixel, but we can specify
17589      * that it move a number of pixels at a time.  This method resolves the
17590      * location when we have it set up like this.
17591      * @method getTick
17592      * @param {int} val where we want to place the object
17593      * @param {int[]} tickArray sorted array of valid points
17594      * @return {int} the closest tick
17595      * @private
17596      */
17597     getTick: function(val, tickArray) {
17598
17599         if (!tickArray) {
17600             // If tick interval is not defined, it is effectively 1 pixel,
17601             // so we return the value passed to us.
17602             return val;
17603         } else if (tickArray[0] >= val) {
17604             // The value is lower than the first tick, so we return the first
17605             // tick.
17606             return tickArray[0];
17607         } else {
17608             for (var i=0, len=tickArray.length; i<len; ++i) {
17609                 var next = i + 1;
17610                 if (tickArray[next] && tickArray[next] >= val) {
17611                     var diff1 = val - tickArray[i];
17612                     var diff2 = tickArray[next] - val;
17613                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17614                 }
17615             }
17616
17617             // The value is larger than the last tick, so we return the last
17618             // tick.
17619             return tickArray[tickArray.length - 1];
17620         }
17621     },
17622
17623     /**
17624      * toString method
17625      * @method toString
17626      * @return {string} string representation of the dd obj
17627      */
17628     toString: function() {
17629         return ("DragDrop " + this.id);
17630     }
17631
17632 });
17633
17634 })();
17635 /*
17636  * Based on:
17637  * Ext JS Library 1.1.1
17638  * Copyright(c) 2006-2007, Ext JS, LLC.
17639  *
17640  * Originally Released Under LGPL - original licence link has changed is not relivant.
17641  *
17642  * Fork - LGPL
17643  * <script type="text/javascript">
17644  */
17645
17646
17647 /**
17648  * The drag and drop utility provides a framework for building drag and drop
17649  * applications.  In addition to enabling drag and drop for specific elements,
17650  * the drag and drop elements are tracked by the manager class, and the
17651  * interactions between the various elements are tracked during the drag and
17652  * the implementing code is notified about these important moments.
17653  */
17654
17655 // Only load the library once.  Rewriting the manager class would orphan
17656 // existing drag and drop instances.
17657 if (!Roo.dd.DragDropMgr) {
17658
17659 /**
17660  * @class Roo.dd.DragDropMgr
17661  * DragDropMgr is a singleton that tracks the element interaction for
17662  * all DragDrop items in the window.  Generally, you will not call
17663  * this class directly, but it does have helper methods that could
17664  * be useful in your DragDrop implementations.
17665  * @singleton
17666  */
17667 Roo.dd.DragDropMgr = function() {
17668
17669     var Event = Roo.EventManager;
17670
17671     return {
17672
17673         /**
17674          * Two dimensional Array of registered DragDrop objects.  The first
17675          * dimension is the DragDrop item group, the second the DragDrop
17676          * object.
17677          * @property ids
17678          * @type {string: string}
17679          * @private
17680          * @static
17681          */
17682         ids: {},
17683
17684         /**
17685          * Array of element ids defined as drag handles.  Used to determine
17686          * if the element that generated the mousedown event is actually the
17687          * handle and not the html element itself.
17688          * @property handleIds
17689          * @type {string: string}
17690          * @private
17691          * @static
17692          */
17693         handleIds: {},
17694
17695         /**
17696          * the DragDrop object that is currently being dragged
17697          * @property dragCurrent
17698          * @type DragDrop
17699          * @private
17700          * @static
17701          **/
17702         dragCurrent: null,
17703
17704         /**
17705          * the DragDrop object(s) that are being hovered over
17706          * @property dragOvers
17707          * @type Array
17708          * @private
17709          * @static
17710          */
17711         dragOvers: {},
17712
17713         /**
17714          * the X distance between the cursor and the object being dragged
17715          * @property deltaX
17716          * @type int
17717          * @private
17718          * @static
17719          */
17720         deltaX: 0,
17721
17722         /**
17723          * the Y distance between the cursor and the object being dragged
17724          * @property deltaY
17725          * @type int
17726          * @private
17727          * @static
17728          */
17729         deltaY: 0,
17730
17731         /**
17732          * Flag to determine if we should prevent the default behavior of the
17733          * events we define. By default this is true, but this can be set to
17734          * false if you need the default behavior (not recommended)
17735          * @property preventDefault
17736          * @type boolean
17737          * @static
17738          */
17739         preventDefault: true,
17740
17741         /**
17742          * Flag to determine if we should stop the propagation of the events
17743          * we generate. This is true by default but you may want to set it to
17744          * false if the html element contains other features that require the
17745          * mouse click.
17746          * @property stopPropagation
17747          * @type boolean
17748          * @static
17749          */
17750         stopPropagation: true,
17751
17752         /**
17753          * Internal flag that is set to true when drag and drop has been
17754          * intialized
17755          * @property initialized
17756          * @private
17757          * @static
17758          */
17759         initalized: false,
17760
17761         /**
17762          * All drag and drop can be disabled.
17763          * @property locked
17764          * @private
17765          * @static
17766          */
17767         locked: false,
17768
17769         /**
17770          * Called the first time an element is registered.
17771          * @method init
17772          * @private
17773          * @static
17774          */
17775         init: function() {
17776             this.initialized = true;
17777         },
17778
17779         /**
17780          * In point mode, drag and drop interaction is defined by the
17781          * location of the cursor during the drag/drop
17782          * @property POINT
17783          * @type int
17784          * @static
17785          */
17786         POINT: 0,
17787
17788         /**
17789          * In intersect mode, drag and drop interactio nis defined by the
17790          * overlap of two or more drag and drop objects.
17791          * @property INTERSECT
17792          * @type int
17793          * @static
17794          */
17795         INTERSECT: 1,
17796
17797         /**
17798          * The current drag and drop mode.  Default: POINT
17799          * @property mode
17800          * @type int
17801          * @static
17802          */
17803         mode: 0,
17804
17805         /**
17806          * Runs method on all drag and drop objects
17807          * @method _execOnAll
17808          * @private
17809          * @static
17810          */
17811         _execOnAll: function(sMethod, args) {
17812             for (var i in this.ids) {
17813                 for (var j in this.ids[i]) {
17814                     var oDD = this.ids[i][j];
17815                     if (! this.isTypeOfDD(oDD)) {
17816                         continue;
17817                     }
17818                     oDD[sMethod].apply(oDD, args);
17819                 }
17820             }
17821         },
17822
17823         /**
17824          * Drag and drop initialization.  Sets up the global event handlers
17825          * @method _onLoad
17826          * @private
17827          * @static
17828          */
17829         _onLoad: function() {
17830
17831             this.init();
17832
17833             if (!Roo.isTouch) {
17834                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17835                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17836             }
17837             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17838             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17839             
17840             Event.on(window,   "unload",    this._onUnload, this, true);
17841             Event.on(window,   "resize",    this._onResize, this, true);
17842             // Event.on(window,   "mouseout",    this._test);
17843
17844         },
17845
17846         /**
17847          * Reset constraints on all drag and drop objs
17848          * @method _onResize
17849          * @private
17850          * @static
17851          */
17852         _onResize: function(e) {
17853             this._execOnAll("resetConstraints", []);
17854         },
17855
17856         /**
17857          * Lock all drag and drop functionality
17858          * @method lock
17859          * @static
17860          */
17861         lock: function() { this.locked = true; },
17862
17863         /**
17864          * Unlock all drag and drop functionality
17865          * @method unlock
17866          * @static
17867          */
17868         unlock: function() { this.locked = false; },
17869
17870         /**
17871          * Is drag and drop locked?
17872          * @method isLocked
17873          * @return {boolean} True if drag and drop is locked, false otherwise.
17874          * @static
17875          */
17876         isLocked: function() { return this.locked; },
17877
17878         /**
17879          * Location cache that is set for all drag drop objects when a drag is
17880          * initiated, cleared when the drag is finished.
17881          * @property locationCache
17882          * @private
17883          * @static
17884          */
17885         locationCache: {},
17886
17887         /**
17888          * Set useCache to false if you want to force object the lookup of each
17889          * drag and drop linked element constantly during a drag.
17890          * @property useCache
17891          * @type boolean
17892          * @static
17893          */
17894         useCache: true,
17895
17896         /**
17897          * The number of pixels that the mouse needs to move after the
17898          * mousedown before the drag is initiated.  Default=3;
17899          * @property clickPixelThresh
17900          * @type int
17901          * @static
17902          */
17903         clickPixelThresh: 3,
17904
17905         /**
17906          * The number of milliseconds after the mousedown event to initiate the
17907          * drag if we don't get a mouseup event. Default=1000
17908          * @property clickTimeThresh
17909          * @type int
17910          * @static
17911          */
17912         clickTimeThresh: 350,
17913
17914         /**
17915          * Flag that indicates that either the drag pixel threshold or the
17916          * mousdown time threshold has been met
17917          * @property dragThreshMet
17918          * @type boolean
17919          * @private
17920          * @static
17921          */
17922         dragThreshMet: false,
17923
17924         /**
17925          * Timeout used for the click time threshold
17926          * @property clickTimeout
17927          * @type Object
17928          * @private
17929          * @static
17930          */
17931         clickTimeout: null,
17932
17933         /**
17934          * The X position of the mousedown event stored for later use when a
17935          * drag threshold is met.
17936          * @property startX
17937          * @type int
17938          * @private
17939          * @static
17940          */
17941         startX: 0,
17942
17943         /**
17944          * The Y position of the mousedown event stored for later use when a
17945          * drag threshold is met.
17946          * @property startY
17947          * @type int
17948          * @private
17949          * @static
17950          */
17951         startY: 0,
17952
17953         /**
17954          * Each DragDrop instance must be registered with the DragDropMgr.
17955          * This is executed in DragDrop.init()
17956          * @method regDragDrop
17957          * @param {DragDrop} oDD the DragDrop object to register
17958          * @param {String} sGroup the name of the group this element belongs to
17959          * @static
17960          */
17961         regDragDrop: function(oDD, sGroup) {
17962             if (!this.initialized) { this.init(); }
17963
17964             if (!this.ids[sGroup]) {
17965                 this.ids[sGroup] = {};
17966             }
17967             this.ids[sGroup][oDD.id] = oDD;
17968         },
17969
17970         /**
17971          * Removes the supplied dd instance from the supplied group. Executed
17972          * by DragDrop.removeFromGroup, so don't call this function directly.
17973          * @method removeDDFromGroup
17974          * @private
17975          * @static
17976          */
17977         removeDDFromGroup: function(oDD, sGroup) {
17978             if (!this.ids[sGroup]) {
17979                 this.ids[sGroup] = {};
17980             }
17981
17982             var obj = this.ids[sGroup];
17983             if (obj && obj[oDD.id]) {
17984                 delete obj[oDD.id];
17985             }
17986         },
17987
17988         /**
17989          * Unregisters a drag and drop item.  This is executed in
17990          * DragDrop.unreg, use that method instead of calling this directly.
17991          * @method _remove
17992          * @private
17993          * @static
17994          */
17995         _remove: function(oDD) {
17996             for (var g in oDD.groups) {
17997                 if (g && this.ids[g][oDD.id]) {
17998                     delete this.ids[g][oDD.id];
17999                 }
18000             }
18001             delete this.handleIds[oDD.id];
18002         },
18003
18004         /**
18005          * Each DragDrop handle element must be registered.  This is done
18006          * automatically when executing DragDrop.setHandleElId()
18007          * @method regHandle
18008          * @param {String} sDDId the DragDrop id this element is a handle for
18009          * @param {String} sHandleId the id of the element that is the drag
18010          * handle
18011          * @static
18012          */
18013         regHandle: function(sDDId, sHandleId) {
18014             if (!this.handleIds[sDDId]) {
18015                 this.handleIds[sDDId] = {};
18016             }
18017             this.handleIds[sDDId][sHandleId] = sHandleId;
18018         },
18019
18020         /**
18021          * Utility function to determine if a given element has been
18022          * registered as a drag drop item.
18023          * @method isDragDrop
18024          * @param {String} id the element id to check
18025          * @return {boolean} true if this element is a DragDrop item,
18026          * false otherwise
18027          * @static
18028          */
18029         isDragDrop: function(id) {
18030             return ( this.getDDById(id) ) ? true : false;
18031         },
18032
18033         /**
18034          * Returns the drag and drop instances that are in all groups the
18035          * passed in instance belongs to.
18036          * @method getRelated
18037          * @param {DragDrop} p_oDD the obj to get related data for
18038          * @param {boolean} bTargetsOnly if true, only return targetable objs
18039          * @return {DragDrop[]} the related instances
18040          * @static
18041          */
18042         getRelated: function(p_oDD, bTargetsOnly) {
18043             var oDDs = [];
18044             for (var i in p_oDD.groups) {
18045                 for (j in this.ids[i]) {
18046                     var dd = this.ids[i][j];
18047                     if (! this.isTypeOfDD(dd)) {
18048                         continue;
18049                     }
18050                     if (!bTargetsOnly || dd.isTarget) {
18051                         oDDs[oDDs.length] = dd;
18052                     }
18053                 }
18054             }
18055
18056             return oDDs;
18057         },
18058
18059         /**
18060          * Returns true if the specified dd target is a legal target for
18061          * the specifice drag obj
18062          * @method isLegalTarget
18063          * @param {DragDrop} the drag obj
18064          * @param {DragDrop} the target
18065          * @return {boolean} true if the target is a legal target for the
18066          * dd obj
18067          * @static
18068          */
18069         isLegalTarget: function (oDD, oTargetDD) {
18070             var targets = this.getRelated(oDD, true);
18071             for (var i=0, len=targets.length;i<len;++i) {
18072                 if (targets[i].id == oTargetDD.id) {
18073                     return true;
18074                 }
18075             }
18076
18077             return false;
18078         },
18079
18080         /**
18081          * My goal is to be able to transparently determine if an object is
18082          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18083          * returns "object", oDD.constructor.toString() always returns
18084          * "DragDrop" and not the name of the subclass.  So for now it just
18085          * evaluates a well-known variable in DragDrop.
18086          * @method isTypeOfDD
18087          * @param {Object} the object to evaluate
18088          * @return {boolean} true if typeof oDD = DragDrop
18089          * @static
18090          */
18091         isTypeOfDD: function (oDD) {
18092             return (oDD && oDD.__ygDragDrop);
18093         },
18094
18095         /**
18096          * Utility function to determine if a given element has been
18097          * registered as a drag drop handle for the given Drag Drop object.
18098          * @method isHandle
18099          * @param {String} id the element id to check
18100          * @return {boolean} true if this element is a DragDrop handle, false
18101          * otherwise
18102          * @static
18103          */
18104         isHandle: function(sDDId, sHandleId) {
18105             return ( this.handleIds[sDDId] &&
18106                             this.handleIds[sDDId][sHandleId] );
18107         },
18108
18109         /**
18110          * Returns the DragDrop instance for a given id
18111          * @method getDDById
18112          * @param {String} id the id of the DragDrop object
18113          * @return {DragDrop} the drag drop object, null if it is not found
18114          * @static
18115          */
18116         getDDById: function(id) {
18117             for (var i in this.ids) {
18118                 if (this.ids[i][id]) {
18119                     return this.ids[i][id];
18120                 }
18121             }
18122             return null;
18123         },
18124
18125         /**
18126          * Fired after a registered DragDrop object gets the mousedown event.
18127          * Sets up the events required to track the object being dragged
18128          * @method handleMouseDown
18129          * @param {Event} e the event
18130          * @param oDD the DragDrop object being dragged
18131          * @private
18132          * @static
18133          */
18134         handleMouseDown: function(e, oDD) {
18135             if(Roo.QuickTips){
18136                 Roo.QuickTips.disable();
18137             }
18138             this.currentTarget = e.getTarget();
18139
18140             this.dragCurrent = oDD;
18141
18142             var el = oDD.getEl();
18143
18144             // track start position
18145             this.startX = e.getPageX();
18146             this.startY = e.getPageY();
18147
18148             this.deltaX = this.startX - el.offsetLeft;
18149             this.deltaY = this.startY - el.offsetTop;
18150
18151             this.dragThreshMet = false;
18152
18153             this.clickTimeout = setTimeout(
18154                     function() {
18155                         var DDM = Roo.dd.DDM;
18156                         DDM.startDrag(DDM.startX, DDM.startY);
18157                     },
18158                     this.clickTimeThresh );
18159         },
18160
18161         /**
18162          * Fired when either the drag pixel threshol or the mousedown hold
18163          * time threshold has been met.
18164          * @method startDrag
18165          * @param x {int} the X position of the original mousedown
18166          * @param y {int} the Y position of the original mousedown
18167          * @static
18168          */
18169         startDrag: function(x, y) {
18170             clearTimeout(this.clickTimeout);
18171             if (this.dragCurrent) {
18172                 this.dragCurrent.b4StartDrag(x, y);
18173                 this.dragCurrent.startDrag(x, y);
18174             }
18175             this.dragThreshMet = true;
18176         },
18177
18178         /**
18179          * Internal function to handle the mouseup event.  Will be invoked
18180          * from the context of the document.
18181          * @method handleMouseUp
18182          * @param {Event} e the event
18183          * @private
18184          * @static
18185          */
18186         handleMouseUp: function(e) {
18187
18188             if(Roo.QuickTips){
18189                 Roo.QuickTips.enable();
18190             }
18191             if (! this.dragCurrent) {
18192                 return;
18193             }
18194
18195             clearTimeout(this.clickTimeout);
18196
18197             if (this.dragThreshMet) {
18198                 this.fireEvents(e, true);
18199             } else {
18200             }
18201
18202             this.stopDrag(e);
18203
18204             this.stopEvent(e);
18205         },
18206
18207         /**
18208          * Utility to stop event propagation and event default, if these
18209          * features are turned on.
18210          * @method stopEvent
18211          * @param {Event} e the event as returned by this.getEvent()
18212          * @static
18213          */
18214         stopEvent: function(e){
18215             if(this.stopPropagation) {
18216                 e.stopPropagation();
18217             }
18218
18219             if (this.preventDefault) {
18220                 e.preventDefault();
18221             }
18222         },
18223
18224         /**
18225          * Internal function to clean up event handlers after the drag
18226          * operation is complete
18227          * @method stopDrag
18228          * @param {Event} e the event
18229          * @private
18230          * @static
18231          */
18232         stopDrag: function(e) {
18233             // Fire the drag end event for the item that was dragged
18234             if (this.dragCurrent) {
18235                 if (this.dragThreshMet) {
18236                     this.dragCurrent.b4EndDrag(e);
18237                     this.dragCurrent.endDrag(e);
18238                 }
18239
18240                 this.dragCurrent.onMouseUp(e);
18241             }
18242
18243             this.dragCurrent = null;
18244             this.dragOvers = {};
18245         },
18246
18247         /**
18248          * Internal function to handle the mousemove event.  Will be invoked
18249          * from the context of the html element.
18250          *
18251          * @TODO figure out what we can do about mouse events lost when the
18252          * user drags objects beyond the window boundary.  Currently we can
18253          * detect this in internet explorer by verifying that the mouse is
18254          * down during the mousemove event.  Firefox doesn't give us the
18255          * button state on the mousemove event.
18256          * @method handleMouseMove
18257          * @param {Event} e the event
18258          * @private
18259          * @static
18260          */
18261         handleMouseMove: function(e) {
18262             if (! this.dragCurrent) {
18263                 return true;
18264             }
18265
18266             // var button = e.which || e.button;
18267
18268             // check for IE mouseup outside of page boundary
18269             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18270                 this.stopEvent(e);
18271                 return this.handleMouseUp(e);
18272             }
18273
18274             if (!this.dragThreshMet) {
18275                 var diffX = Math.abs(this.startX - e.getPageX());
18276                 var diffY = Math.abs(this.startY - e.getPageY());
18277                 if (diffX > this.clickPixelThresh ||
18278                             diffY > this.clickPixelThresh) {
18279                     this.startDrag(this.startX, this.startY);
18280                 }
18281             }
18282
18283             if (this.dragThreshMet) {
18284                 this.dragCurrent.b4Drag(e);
18285                 this.dragCurrent.onDrag(e);
18286                 if(!this.dragCurrent.moveOnly){
18287                     this.fireEvents(e, false);
18288                 }
18289             }
18290
18291             this.stopEvent(e);
18292
18293             return true;
18294         },
18295
18296         /**
18297          * Iterates over all of the DragDrop elements to find ones we are
18298          * hovering over or dropping on
18299          * @method fireEvents
18300          * @param {Event} e the event
18301          * @param {boolean} isDrop is this a drop op or a mouseover op?
18302          * @private
18303          * @static
18304          */
18305         fireEvents: function(e, isDrop) {
18306             var dc = this.dragCurrent;
18307
18308             // If the user did the mouse up outside of the window, we could
18309             // get here even though we have ended the drag.
18310             if (!dc || dc.isLocked()) {
18311                 return;
18312             }
18313
18314             var pt = e.getPoint();
18315
18316             // cache the previous dragOver array
18317             var oldOvers = [];
18318
18319             var outEvts   = [];
18320             var overEvts  = [];
18321             var dropEvts  = [];
18322             var enterEvts = [];
18323
18324             // Check to see if the object(s) we were hovering over is no longer
18325             // being hovered over so we can fire the onDragOut event
18326             for (var i in this.dragOvers) {
18327
18328                 var ddo = this.dragOvers[i];
18329
18330                 if (! this.isTypeOfDD(ddo)) {
18331                     continue;
18332                 }
18333
18334                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18335                     outEvts.push( ddo );
18336                 }
18337
18338                 oldOvers[i] = true;
18339                 delete this.dragOvers[i];
18340             }
18341
18342             for (var sGroup in dc.groups) {
18343
18344                 if ("string" != typeof sGroup) {
18345                     continue;
18346                 }
18347
18348                 for (i in this.ids[sGroup]) {
18349                     var oDD = this.ids[sGroup][i];
18350                     if (! this.isTypeOfDD(oDD)) {
18351                         continue;
18352                     }
18353
18354                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18355                         if (this.isOverTarget(pt, oDD, this.mode)) {
18356                             // look for drop interactions
18357                             if (isDrop) {
18358                                 dropEvts.push( oDD );
18359                             // look for drag enter and drag over interactions
18360                             } else {
18361
18362                                 // initial drag over: dragEnter fires
18363                                 if (!oldOvers[oDD.id]) {
18364                                     enterEvts.push( oDD );
18365                                 // subsequent drag overs: dragOver fires
18366                                 } else {
18367                                     overEvts.push( oDD );
18368                                 }
18369
18370                                 this.dragOvers[oDD.id] = oDD;
18371                             }
18372                         }
18373                     }
18374                 }
18375             }
18376
18377             if (this.mode) {
18378                 if (outEvts.length) {
18379                     dc.b4DragOut(e, outEvts);
18380                     dc.onDragOut(e, outEvts);
18381                 }
18382
18383                 if (enterEvts.length) {
18384                     dc.onDragEnter(e, enterEvts);
18385                 }
18386
18387                 if (overEvts.length) {
18388                     dc.b4DragOver(e, overEvts);
18389                     dc.onDragOver(e, overEvts);
18390                 }
18391
18392                 if (dropEvts.length) {
18393                     dc.b4DragDrop(e, dropEvts);
18394                     dc.onDragDrop(e, dropEvts);
18395                 }
18396
18397             } else {
18398                 // fire dragout events
18399                 var len = 0;
18400                 for (i=0, len=outEvts.length; i<len; ++i) {
18401                     dc.b4DragOut(e, outEvts[i].id);
18402                     dc.onDragOut(e, outEvts[i].id);
18403                 }
18404
18405                 // fire enter events
18406                 for (i=0,len=enterEvts.length; i<len; ++i) {
18407                     // dc.b4DragEnter(e, oDD.id);
18408                     dc.onDragEnter(e, enterEvts[i].id);
18409                 }
18410
18411                 // fire over events
18412                 for (i=0,len=overEvts.length; i<len; ++i) {
18413                     dc.b4DragOver(e, overEvts[i].id);
18414                     dc.onDragOver(e, overEvts[i].id);
18415                 }
18416
18417                 // fire drop events
18418                 for (i=0, len=dropEvts.length; i<len; ++i) {
18419                     dc.b4DragDrop(e, dropEvts[i].id);
18420                     dc.onDragDrop(e, dropEvts[i].id);
18421                 }
18422
18423             }
18424
18425             // notify about a drop that did not find a target
18426             if (isDrop && !dropEvts.length) {
18427                 dc.onInvalidDrop(e);
18428             }
18429
18430         },
18431
18432         /**
18433          * Helper function for getting the best match from the list of drag
18434          * and drop objects returned by the drag and drop events when we are
18435          * in INTERSECT mode.  It returns either the first object that the
18436          * cursor is over, or the object that has the greatest overlap with
18437          * the dragged element.
18438          * @method getBestMatch
18439          * @param  {DragDrop[]} dds The array of drag and drop objects
18440          * targeted
18441          * @return {DragDrop}       The best single match
18442          * @static
18443          */
18444         getBestMatch: function(dds) {
18445             var winner = null;
18446             // Return null if the input is not what we expect
18447             //if (!dds || !dds.length || dds.length == 0) {
18448                // winner = null;
18449             // If there is only one item, it wins
18450             //} else if (dds.length == 1) {
18451
18452             var len = dds.length;
18453
18454             if (len == 1) {
18455                 winner = dds[0];
18456             } else {
18457                 // Loop through the targeted items
18458                 for (var i=0; i<len; ++i) {
18459                     var dd = dds[i];
18460                     // If the cursor is over the object, it wins.  If the
18461                     // cursor is over multiple matches, the first one we come
18462                     // to wins.
18463                     if (dd.cursorIsOver) {
18464                         winner = dd;
18465                         break;
18466                     // Otherwise the object with the most overlap wins
18467                     } else {
18468                         if (!winner ||
18469                             winner.overlap.getArea() < dd.overlap.getArea()) {
18470                             winner = dd;
18471                         }
18472                     }
18473                 }
18474             }
18475
18476             return winner;
18477         },
18478
18479         /**
18480          * Refreshes the cache of the top-left and bottom-right points of the
18481          * drag and drop objects in the specified group(s).  This is in the
18482          * format that is stored in the drag and drop instance, so typical
18483          * usage is:
18484          * <code>
18485          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18486          * </code>
18487          * Alternatively:
18488          * <code>
18489          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18490          * </code>
18491          * @TODO this really should be an indexed array.  Alternatively this
18492          * method could accept both.
18493          * @method refreshCache
18494          * @param {Object} groups an associative array of groups to refresh
18495          * @static
18496          */
18497         refreshCache: function(groups) {
18498             for (var sGroup in groups) {
18499                 if ("string" != typeof sGroup) {
18500                     continue;
18501                 }
18502                 for (var i in this.ids[sGroup]) {
18503                     var oDD = this.ids[sGroup][i];
18504
18505                     if (this.isTypeOfDD(oDD)) {
18506                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18507                         var loc = this.getLocation(oDD);
18508                         if (loc) {
18509                             this.locationCache[oDD.id] = loc;
18510                         } else {
18511                             delete this.locationCache[oDD.id];
18512                             // this will unregister the drag and drop object if
18513                             // the element is not in a usable state
18514                             // oDD.unreg();
18515                         }
18516                     }
18517                 }
18518             }
18519         },
18520
18521         /**
18522          * This checks to make sure an element exists and is in the DOM.  The
18523          * main purpose is to handle cases where innerHTML is used to remove
18524          * drag and drop objects from the DOM.  IE provides an 'unspecified
18525          * error' when trying to access the offsetParent of such an element
18526          * @method verifyEl
18527          * @param {HTMLElement} el the element to check
18528          * @return {boolean} true if the element looks usable
18529          * @static
18530          */
18531         verifyEl: function(el) {
18532             if (el) {
18533                 var parent;
18534                 if(Roo.isIE){
18535                     try{
18536                         parent = el.offsetParent;
18537                     }catch(e){}
18538                 }else{
18539                     parent = el.offsetParent;
18540                 }
18541                 if (parent) {
18542                     return true;
18543                 }
18544             }
18545
18546             return false;
18547         },
18548
18549         /**
18550          * Returns a Region object containing the drag and drop element's position
18551          * and size, including the padding configured for it
18552          * @method getLocation
18553          * @param {DragDrop} oDD the drag and drop object to get the
18554          *                       location for
18555          * @return {Roo.lib.Region} a Region object representing the total area
18556          *                             the element occupies, including any padding
18557          *                             the instance is configured for.
18558          * @static
18559          */
18560         getLocation: function(oDD) {
18561             if (! this.isTypeOfDD(oDD)) {
18562                 return null;
18563             }
18564
18565             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18566
18567             try {
18568                 pos= Roo.lib.Dom.getXY(el);
18569             } catch (e) { }
18570
18571             if (!pos) {
18572                 return null;
18573             }
18574
18575             x1 = pos[0];
18576             x2 = x1 + el.offsetWidth;
18577             y1 = pos[1];
18578             y2 = y1 + el.offsetHeight;
18579
18580             t = y1 - oDD.padding[0];
18581             r = x2 + oDD.padding[1];
18582             b = y2 + oDD.padding[2];
18583             l = x1 - oDD.padding[3];
18584
18585             return new Roo.lib.Region( t, r, b, l );
18586         },
18587
18588         /**
18589          * Checks the cursor location to see if it over the target
18590          * @method isOverTarget
18591          * @param {Roo.lib.Point} pt The point to evaluate
18592          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18593          * @return {boolean} true if the mouse is over the target
18594          * @private
18595          * @static
18596          */
18597         isOverTarget: function(pt, oTarget, intersect) {
18598             // use cache if available
18599             var loc = this.locationCache[oTarget.id];
18600             if (!loc || !this.useCache) {
18601                 loc = this.getLocation(oTarget);
18602                 this.locationCache[oTarget.id] = loc;
18603
18604             }
18605
18606             if (!loc) {
18607                 return false;
18608             }
18609
18610             oTarget.cursorIsOver = loc.contains( pt );
18611
18612             // DragDrop is using this as a sanity check for the initial mousedown
18613             // in this case we are done.  In POINT mode, if the drag obj has no
18614             // contraints, we are also done. Otherwise we need to evaluate the
18615             // location of the target as related to the actual location of the
18616             // dragged element.
18617             var dc = this.dragCurrent;
18618             if (!dc || !dc.getTargetCoord ||
18619                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18620                 return oTarget.cursorIsOver;
18621             }
18622
18623             oTarget.overlap = null;
18624
18625             // Get the current location of the drag element, this is the
18626             // location of the mouse event less the delta that represents
18627             // where the original mousedown happened on the element.  We
18628             // need to consider constraints and ticks as well.
18629             var pos = dc.getTargetCoord(pt.x, pt.y);
18630
18631             var el = dc.getDragEl();
18632             var curRegion = new Roo.lib.Region( pos.y,
18633                                                    pos.x + el.offsetWidth,
18634                                                    pos.y + el.offsetHeight,
18635                                                    pos.x );
18636
18637             var overlap = curRegion.intersect(loc);
18638
18639             if (overlap) {
18640                 oTarget.overlap = overlap;
18641                 return (intersect) ? true : oTarget.cursorIsOver;
18642             } else {
18643                 return false;
18644             }
18645         },
18646
18647         /**
18648          * unload event handler
18649          * @method _onUnload
18650          * @private
18651          * @static
18652          */
18653         _onUnload: function(e, me) {
18654             Roo.dd.DragDropMgr.unregAll();
18655         },
18656
18657         /**
18658          * Cleans up the drag and drop events and objects.
18659          * @method unregAll
18660          * @private
18661          * @static
18662          */
18663         unregAll: function() {
18664
18665             if (this.dragCurrent) {
18666                 this.stopDrag();
18667                 this.dragCurrent = null;
18668             }
18669
18670             this._execOnAll("unreg", []);
18671
18672             for (i in this.elementCache) {
18673                 delete this.elementCache[i];
18674             }
18675
18676             this.elementCache = {};
18677             this.ids = {};
18678         },
18679
18680         /**
18681          * A cache of DOM elements
18682          * @property elementCache
18683          * @private
18684          * @static
18685          */
18686         elementCache: {},
18687
18688         /**
18689          * Get the wrapper for the DOM element specified
18690          * @method getElWrapper
18691          * @param {String} id the id of the element to get
18692          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18693          * @private
18694          * @deprecated This wrapper isn't that useful
18695          * @static
18696          */
18697         getElWrapper: function(id) {
18698             var oWrapper = this.elementCache[id];
18699             if (!oWrapper || !oWrapper.el) {
18700                 oWrapper = this.elementCache[id] =
18701                     new this.ElementWrapper(Roo.getDom(id));
18702             }
18703             return oWrapper;
18704         },
18705
18706         /**
18707          * Returns the actual DOM element
18708          * @method getElement
18709          * @param {String} id the id of the elment to get
18710          * @return {Object} The element
18711          * @deprecated use Roo.getDom instead
18712          * @static
18713          */
18714         getElement: function(id) {
18715             return Roo.getDom(id);
18716         },
18717
18718         /**
18719          * Returns the style property for the DOM element (i.e.,
18720          * document.getElById(id).style)
18721          * @method getCss
18722          * @param {String} id the id of the elment to get
18723          * @return {Object} The style property of the element
18724          * @deprecated use Roo.getDom instead
18725          * @static
18726          */
18727         getCss: function(id) {
18728             var el = Roo.getDom(id);
18729             return (el) ? el.style : null;
18730         },
18731
18732         /**
18733          * Inner class for cached elements
18734          * @class DragDropMgr.ElementWrapper
18735          * @for DragDropMgr
18736          * @private
18737          * @deprecated
18738          */
18739         ElementWrapper: function(el) {
18740                 /**
18741                  * The element
18742                  * @property el
18743                  */
18744                 this.el = el || null;
18745                 /**
18746                  * The element id
18747                  * @property id
18748                  */
18749                 this.id = this.el && el.id;
18750                 /**
18751                  * A reference to the style property
18752                  * @property css
18753                  */
18754                 this.css = this.el && el.style;
18755             },
18756
18757         /**
18758          * Returns the X position of an html element
18759          * @method getPosX
18760          * @param el the element for which to get the position
18761          * @return {int} the X coordinate
18762          * @for DragDropMgr
18763          * @deprecated use Roo.lib.Dom.getX instead
18764          * @static
18765          */
18766         getPosX: function(el) {
18767             return Roo.lib.Dom.getX(el);
18768         },
18769
18770         /**
18771          * Returns the Y position of an html element
18772          * @method getPosY
18773          * @param el the element for which to get the position
18774          * @return {int} the Y coordinate
18775          * @deprecated use Roo.lib.Dom.getY instead
18776          * @static
18777          */
18778         getPosY: function(el) {
18779             return Roo.lib.Dom.getY(el);
18780         },
18781
18782         /**
18783          * Swap two nodes.  In IE, we use the native method, for others we
18784          * emulate the IE behavior
18785          * @method swapNode
18786          * @param n1 the first node to swap
18787          * @param n2 the other node to swap
18788          * @static
18789          */
18790         swapNode: function(n1, n2) {
18791             if (n1.swapNode) {
18792                 n1.swapNode(n2);
18793             } else {
18794                 var p = n2.parentNode;
18795                 var s = n2.nextSibling;
18796
18797                 if (s == n1) {
18798                     p.insertBefore(n1, n2);
18799                 } else if (n2 == n1.nextSibling) {
18800                     p.insertBefore(n2, n1);
18801                 } else {
18802                     n1.parentNode.replaceChild(n2, n1);
18803                     p.insertBefore(n1, s);
18804                 }
18805             }
18806         },
18807
18808         /**
18809          * Returns the current scroll position
18810          * @method getScroll
18811          * @private
18812          * @static
18813          */
18814         getScroll: function () {
18815             var t, l, dde=document.documentElement, db=document.body;
18816             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18817                 t = dde.scrollTop;
18818                 l = dde.scrollLeft;
18819             } else if (db) {
18820                 t = db.scrollTop;
18821                 l = db.scrollLeft;
18822             } else {
18823
18824             }
18825             return { top: t, left: l };
18826         },
18827
18828         /**
18829          * Returns the specified element style property
18830          * @method getStyle
18831          * @param {HTMLElement} el          the element
18832          * @param {string}      styleProp   the style property
18833          * @return {string} The value of the style property
18834          * @deprecated use Roo.lib.Dom.getStyle
18835          * @static
18836          */
18837         getStyle: function(el, styleProp) {
18838             return Roo.fly(el).getStyle(styleProp);
18839         },
18840
18841         /**
18842          * Gets the scrollTop
18843          * @method getScrollTop
18844          * @return {int} the document's scrollTop
18845          * @static
18846          */
18847         getScrollTop: function () { return this.getScroll().top; },
18848
18849         /**
18850          * Gets the scrollLeft
18851          * @method getScrollLeft
18852          * @return {int} the document's scrollTop
18853          * @static
18854          */
18855         getScrollLeft: function () { return this.getScroll().left; },
18856
18857         /**
18858          * Sets the x/y position of an element to the location of the
18859          * target element.
18860          * @method moveToEl
18861          * @param {HTMLElement} moveEl      The element to move
18862          * @param {HTMLElement} targetEl    The position reference element
18863          * @static
18864          */
18865         moveToEl: function (moveEl, targetEl) {
18866             var aCoord = Roo.lib.Dom.getXY(targetEl);
18867             Roo.lib.Dom.setXY(moveEl, aCoord);
18868         },
18869
18870         /**
18871          * Numeric array sort function
18872          * @method numericSort
18873          * @static
18874          */
18875         numericSort: function(a, b) { return (a - b); },
18876
18877         /**
18878          * Internal counter
18879          * @property _timeoutCount
18880          * @private
18881          * @static
18882          */
18883         _timeoutCount: 0,
18884
18885         /**
18886          * Trying to make the load order less important.  Without this we get
18887          * an error if this file is loaded before the Event Utility.
18888          * @method _addListeners
18889          * @private
18890          * @static
18891          */
18892         _addListeners: function() {
18893             var DDM = Roo.dd.DDM;
18894             if ( Roo.lib.Event && document ) {
18895                 DDM._onLoad();
18896             } else {
18897                 if (DDM._timeoutCount > 2000) {
18898                 } else {
18899                     setTimeout(DDM._addListeners, 10);
18900                     if (document && document.body) {
18901                         DDM._timeoutCount += 1;
18902                     }
18903                 }
18904             }
18905         },
18906
18907         /**
18908          * Recursively searches the immediate parent and all child nodes for
18909          * the handle element in order to determine wheter or not it was
18910          * clicked.
18911          * @method handleWasClicked
18912          * @param node the html element to inspect
18913          * @static
18914          */
18915         handleWasClicked: function(node, id) {
18916             if (this.isHandle(id, node.id)) {
18917                 return true;
18918             } else {
18919                 // check to see if this is a text node child of the one we want
18920                 var p = node.parentNode;
18921
18922                 while (p) {
18923                     if (this.isHandle(id, p.id)) {
18924                         return true;
18925                     } else {
18926                         p = p.parentNode;
18927                     }
18928                 }
18929             }
18930
18931             return false;
18932         }
18933
18934     };
18935
18936 }();
18937
18938 // shorter alias, save a few bytes
18939 Roo.dd.DDM = Roo.dd.DragDropMgr;
18940 Roo.dd.DDM._addListeners();
18941
18942 }/*
18943  * Based on:
18944  * Ext JS Library 1.1.1
18945  * Copyright(c) 2006-2007, Ext JS, LLC.
18946  *
18947  * Originally Released Under LGPL - original licence link has changed is not relivant.
18948  *
18949  * Fork - LGPL
18950  * <script type="text/javascript">
18951  */
18952
18953 /**
18954  * @class Roo.dd.DD
18955  * A DragDrop implementation where the linked element follows the
18956  * mouse cursor during a drag.
18957  * @extends Roo.dd.DragDrop
18958  * @constructor
18959  * @param {String} id the id of the linked element
18960  * @param {String} sGroup the group of related DragDrop items
18961  * @param {object} config an object containing configurable attributes
18962  *                Valid properties for DD:
18963  *                    scroll
18964  */
18965 Roo.dd.DD = function(id, sGroup, config) {
18966     if (id) {
18967         this.init(id, sGroup, config);
18968     }
18969 };
18970
18971 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18972
18973     /**
18974      * When set to true, the utility automatically tries to scroll the browser
18975      * window wehn a drag and drop element is dragged near the viewport boundary.
18976      * Defaults to true.
18977      * @property scroll
18978      * @type boolean
18979      */
18980     scroll: true,
18981
18982     /**
18983      * Sets the pointer offset to the distance between the linked element's top
18984      * left corner and the location the element was clicked
18985      * @method autoOffset
18986      * @param {int} iPageX the X coordinate of the click
18987      * @param {int} iPageY the Y coordinate of the click
18988      */
18989     autoOffset: function(iPageX, iPageY) {
18990         var x = iPageX - this.startPageX;
18991         var y = iPageY - this.startPageY;
18992         this.setDelta(x, y);
18993     },
18994
18995     /**
18996      * Sets the pointer offset.  You can call this directly to force the
18997      * offset to be in a particular location (e.g., pass in 0,0 to set it
18998      * to the center of the object)
18999      * @method setDelta
19000      * @param {int} iDeltaX the distance from the left
19001      * @param {int} iDeltaY the distance from the top
19002      */
19003     setDelta: function(iDeltaX, iDeltaY) {
19004         this.deltaX = iDeltaX;
19005         this.deltaY = iDeltaY;
19006     },
19007
19008     /**
19009      * Sets the drag element to the location of the mousedown or click event,
19010      * maintaining the cursor location relative to the location on the element
19011      * that was clicked.  Override this if you want to place the element in a
19012      * location other than where the cursor is.
19013      * @method setDragElPos
19014      * @param {int} iPageX the X coordinate of the mousedown or drag event
19015      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19016      */
19017     setDragElPos: function(iPageX, iPageY) {
19018         // the first time we do this, we are going to check to make sure
19019         // the element has css positioning
19020
19021         var el = this.getDragEl();
19022         this.alignElWithMouse(el, iPageX, iPageY);
19023     },
19024
19025     /**
19026      * Sets the element to the location of the mousedown or click event,
19027      * maintaining the cursor location relative to the location on the element
19028      * that was clicked.  Override this if you want to place the element in a
19029      * location other than where the cursor is.
19030      * @method alignElWithMouse
19031      * @param {HTMLElement} el the element to move
19032      * @param {int} iPageX the X coordinate of the mousedown or drag event
19033      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19034      */
19035     alignElWithMouse: function(el, iPageX, iPageY) {
19036         var oCoord = this.getTargetCoord(iPageX, iPageY);
19037         var fly = el.dom ? el : Roo.fly(el);
19038         if (!this.deltaSetXY) {
19039             var aCoord = [oCoord.x, oCoord.y];
19040             fly.setXY(aCoord);
19041             var newLeft = fly.getLeft(true);
19042             var newTop  = fly.getTop(true);
19043             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19044         } else {
19045             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19046         }
19047
19048         this.cachePosition(oCoord.x, oCoord.y);
19049         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19050         return oCoord;
19051     },
19052
19053     /**
19054      * Saves the most recent position so that we can reset the constraints and
19055      * tick marks on-demand.  We need to know this so that we can calculate the
19056      * number of pixels the element is offset from its original position.
19057      * @method cachePosition
19058      * @param iPageX the current x position (optional, this just makes it so we
19059      * don't have to look it up again)
19060      * @param iPageY the current y position (optional, this just makes it so we
19061      * don't have to look it up again)
19062      */
19063     cachePosition: function(iPageX, iPageY) {
19064         if (iPageX) {
19065             this.lastPageX = iPageX;
19066             this.lastPageY = iPageY;
19067         } else {
19068             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19069             this.lastPageX = aCoord[0];
19070             this.lastPageY = aCoord[1];
19071         }
19072     },
19073
19074     /**
19075      * Auto-scroll the window if the dragged object has been moved beyond the
19076      * visible window boundary.
19077      * @method autoScroll
19078      * @param {int} x the drag element's x position
19079      * @param {int} y the drag element's y position
19080      * @param {int} h the height of the drag element
19081      * @param {int} w the width of the drag element
19082      * @private
19083      */
19084     autoScroll: function(x, y, h, w) {
19085
19086         if (this.scroll) {
19087             // The client height
19088             var clientH = Roo.lib.Dom.getViewWidth();
19089
19090             // The client width
19091             var clientW = Roo.lib.Dom.getViewHeight();
19092
19093             // The amt scrolled down
19094             var st = this.DDM.getScrollTop();
19095
19096             // The amt scrolled right
19097             var sl = this.DDM.getScrollLeft();
19098
19099             // Location of the bottom of the element
19100             var bot = h + y;
19101
19102             // Location of the right of the element
19103             var right = w + x;
19104
19105             // The distance from the cursor to the bottom of the visible area,
19106             // adjusted so that we don't scroll if the cursor is beyond the
19107             // element drag constraints
19108             var toBot = (clientH + st - y - this.deltaY);
19109
19110             // The distance from the cursor to the right of the visible area
19111             var toRight = (clientW + sl - x - this.deltaX);
19112
19113
19114             // How close to the edge the cursor must be before we scroll
19115             // var thresh = (document.all) ? 100 : 40;
19116             var thresh = 40;
19117
19118             // How many pixels to scroll per autoscroll op.  This helps to reduce
19119             // clunky scrolling. IE is more sensitive about this ... it needs this
19120             // value to be higher.
19121             var scrAmt = (document.all) ? 80 : 30;
19122
19123             // Scroll down if we are near the bottom of the visible page and the
19124             // obj extends below the crease
19125             if ( bot > clientH && toBot < thresh ) {
19126                 window.scrollTo(sl, st + scrAmt);
19127             }
19128
19129             // Scroll up if the window is scrolled down and the top of the object
19130             // goes above the top border
19131             if ( y < st && st > 0 && y - st < thresh ) {
19132                 window.scrollTo(sl, st - scrAmt);
19133             }
19134
19135             // Scroll right if the obj is beyond the right border and the cursor is
19136             // near the border.
19137             if ( right > clientW && toRight < thresh ) {
19138                 window.scrollTo(sl + scrAmt, st);
19139             }
19140
19141             // Scroll left if the window has been scrolled to the right and the obj
19142             // extends past the left border
19143             if ( x < sl && sl > 0 && x - sl < thresh ) {
19144                 window.scrollTo(sl - scrAmt, st);
19145             }
19146         }
19147     },
19148
19149     /**
19150      * Finds the location the element should be placed if we want to move
19151      * it to where the mouse location less the click offset would place us.
19152      * @method getTargetCoord
19153      * @param {int} iPageX the X coordinate of the click
19154      * @param {int} iPageY the Y coordinate of the click
19155      * @return an object that contains the coordinates (Object.x and Object.y)
19156      * @private
19157      */
19158     getTargetCoord: function(iPageX, iPageY) {
19159
19160
19161         var x = iPageX - this.deltaX;
19162         var y = iPageY - this.deltaY;
19163
19164         if (this.constrainX) {
19165             if (x < this.minX) { x = this.minX; }
19166             if (x > this.maxX) { x = this.maxX; }
19167         }
19168
19169         if (this.constrainY) {
19170             if (y < this.minY) { y = this.minY; }
19171             if (y > this.maxY) { y = this.maxY; }
19172         }
19173
19174         x = this.getTick(x, this.xTicks);
19175         y = this.getTick(y, this.yTicks);
19176
19177
19178         return {x:x, y:y};
19179     },
19180
19181     /*
19182      * Sets up config options specific to this class. Overrides
19183      * Roo.dd.DragDrop, but all versions of this method through the
19184      * inheritance chain are called
19185      */
19186     applyConfig: function() {
19187         Roo.dd.DD.superclass.applyConfig.call(this);
19188         this.scroll = (this.config.scroll !== false);
19189     },
19190
19191     /*
19192      * Event that fires prior to the onMouseDown event.  Overrides
19193      * Roo.dd.DragDrop.
19194      */
19195     b4MouseDown: function(e) {
19196         // this.resetConstraints();
19197         this.autoOffset(e.getPageX(),
19198                             e.getPageY());
19199     },
19200
19201     /*
19202      * Event that fires prior to the onDrag event.  Overrides
19203      * Roo.dd.DragDrop.
19204      */
19205     b4Drag: function(e) {
19206         this.setDragElPos(e.getPageX(),
19207                             e.getPageY());
19208     },
19209
19210     toString: function() {
19211         return ("DD " + this.id);
19212     }
19213
19214     //////////////////////////////////////////////////////////////////////////
19215     // Debugging ygDragDrop events that can be overridden
19216     //////////////////////////////////////////////////////////////////////////
19217     /*
19218     startDrag: function(x, y) {
19219     },
19220
19221     onDrag: function(e) {
19222     },
19223
19224     onDragEnter: function(e, id) {
19225     },
19226
19227     onDragOver: function(e, id) {
19228     },
19229
19230     onDragOut: function(e, id) {
19231     },
19232
19233     onDragDrop: function(e, id) {
19234     },
19235
19236     endDrag: function(e) {
19237     }
19238
19239     */
19240
19241 });/*
19242  * Based on:
19243  * Ext JS Library 1.1.1
19244  * Copyright(c) 2006-2007, Ext JS, LLC.
19245  *
19246  * Originally Released Under LGPL - original licence link has changed is not relivant.
19247  *
19248  * Fork - LGPL
19249  * <script type="text/javascript">
19250  */
19251
19252 /**
19253  * @class Roo.dd.DDProxy
19254  * A DragDrop implementation that inserts an empty, bordered div into
19255  * the document that follows the cursor during drag operations.  At the time of
19256  * the click, the frame div is resized to the dimensions of the linked html
19257  * element, and moved to the exact location of the linked element.
19258  *
19259  * References to the "frame" element refer to the single proxy element that
19260  * was created to be dragged in place of all DDProxy elements on the
19261  * page.
19262  *
19263  * @extends Roo.dd.DD
19264  * @constructor
19265  * @param {String} id the id of the linked html element
19266  * @param {String} sGroup the group of related DragDrop objects
19267  * @param {object} config an object containing configurable attributes
19268  *                Valid properties for DDProxy in addition to those in DragDrop:
19269  *                   resizeFrame, centerFrame, dragElId
19270  */
19271 Roo.dd.DDProxy = function(id, sGroup, config) {
19272     if (id) {
19273         this.init(id, sGroup, config);
19274         this.initFrame();
19275     }
19276 };
19277
19278 /**
19279  * The default drag frame div id
19280  * @property Roo.dd.DDProxy.dragElId
19281  * @type String
19282  * @static
19283  */
19284 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19285
19286 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19287
19288     /**
19289      * By default we resize the drag frame to be the same size as the element
19290      * we want to drag (this is to get the frame effect).  We can turn it off
19291      * if we want a different behavior.
19292      * @property resizeFrame
19293      * @type boolean
19294      */
19295     resizeFrame: true,
19296
19297     /**
19298      * By default the frame is positioned exactly where the drag element is, so
19299      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19300      * you do not have constraints on the obj is to have the drag frame centered
19301      * around the cursor.  Set centerFrame to true for this effect.
19302      * @property centerFrame
19303      * @type boolean
19304      */
19305     centerFrame: false,
19306
19307     /**
19308      * Creates the proxy element if it does not yet exist
19309      * @method createFrame
19310      */
19311     createFrame: function() {
19312         var self = this;
19313         var body = document.body;
19314
19315         if (!body || !body.firstChild) {
19316             setTimeout( function() { self.createFrame(); }, 50 );
19317             return;
19318         }
19319
19320         var div = this.getDragEl();
19321
19322         if (!div) {
19323             div    = document.createElement("div");
19324             div.id = this.dragElId;
19325             var s  = div.style;
19326
19327             s.position   = "absolute";
19328             s.visibility = "hidden";
19329             s.cursor     = "move";
19330             s.border     = "2px solid #aaa";
19331             s.zIndex     = 999;
19332
19333             // appendChild can blow up IE if invoked prior to the window load event
19334             // while rendering a table.  It is possible there are other scenarios
19335             // that would cause this to happen as well.
19336             body.insertBefore(div, body.firstChild);
19337         }
19338     },
19339
19340     /**
19341      * Initialization for the drag frame element.  Must be called in the
19342      * constructor of all subclasses
19343      * @method initFrame
19344      */
19345     initFrame: function() {
19346         this.createFrame();
19347     },
19348
19349     applyConfig: function() {
19350         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19351
19352         this.resizeFrame = (this.config.resizeFrame !== false);
19353         this.centerFrame = (this.config.centerFrame);
19354         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19355     },
19356
19357     /**
19358      * Resizes the drag frame to the dimensions of the clicked object, positions
19359      * it over the object, and finally displays it
19360      * @method showFrame
19361      * @param {int} iPageX X click position
19362      * @param {int} iPageY Y click position
19363      * @private
19364      */
19365     showFrame: function(iPageX, iPageY) {
19366         var el = this.getEl();
19367         var dragEl = this.getDragEl();
19368         var s = dragEl.style;
19369
19370         this._resizeProxy();
19371
19372         if (this.centerFrame) {
19373             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19374                            Math.round(parseInt(s.height, 10)/2) );
19375         }
19376
19377         this.setDragElPos(iPageX, iPageY);
19378
19379         Roo.fly(dragEl).show();
19380     },
19381
19382     /**
19383      * The proxy is automatically resized to the dimensions of the linked
19384      * element when a drag is initiated, unless resizeFrame is set to false
19385      * @method _resizeProxy
19386      * @private
19387      */
19388     _resizeProxy: function() {
19389         if (this.resizeFrame) {
19390             var el = this.getEl();
19391             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19392         }
19393     },
19394
19395     // overrides Roo.dd.DragDrop
19396     b4MouseDown: function(e) {
19397         var x = e.getPageX();
19398         var y = e.getPageY();
19399         this.autoOffset(x, y);
19400         this.setDragElPos(x, y);
19401     },
19402
19403     // overrides Roo.dd.DragDrop
19404     b4StartDrag: function(x, y) {
19405         // show the drag frame
19406         this.showFrame(x, y);
19407     },
19408
19409     // overrides Roo.dd.DragDrop
19410     b4EndDrag: function(e) {
19411         Roo.fly(this.getDragEl()).hide();
19412     },
19413
19414     // overrides Roo.dd.DragDrop
19415     // By default we try to move the element to the last location of the frame.
19416     // This is so that the default behavior mirrors that of Roo.dd.DD.
19417     endDrag: function(e) {
19418
19419         var lel = this.getEl();
19420         var del = this.getDragEl();
19421
19422         // Show the drag frame briefly so we can get its position
19423         del.style.visibility = "";
19424
19425         this.beforeMove();
19426         // Hide the linked element before the move to get around a Safari
19427         // rendering bug.
19428         lel.style.visibility = "hidden";
19429         Roo.dd.DDM.moveToEl(lel, del);
19430         del.style.visibility = "hidden";
19431         lel.style.visibility = "";
19432
19433         this.afterDrag();
19434     },
19435
19436     beforeMove : function(){
19437
19438     },
19439
19440     afterDrag : function(){
19441
19442     },
19443
19444     toString: function() {
19445         return ("DDProxy " + this.id);
19446     }
19447
19448 });
19449 /*
19450  * Based on:
19451  * Ext JS Library 1.1.1
19452  * Copyright(c) 2006-2007, Ext JS, LLC.
19453  *
19454  * Originally Released Under LGPL - original licence link has changed is not relivant.
19455  *
19456  * Fork - LGPL
19457  * <script type="text/javascript">
19458  */
19459
19460  /**
19461  * @class Roo.dd.DDTarget
19462  * A DragDrop implementation that does not move, but can be a drop
19463  * target.  You would get the same result by simply omitting implementation
19464  * for the event callbacks, but this way we reduce the processing cost of the
19465  * event listener and the callbacks.
19466  * @extends Roo.dd.DragDrop
19467  * @constructor
19468  * @param {String} id the id of the element that is a drop target
19469  * @param {String} sGroup the group of related DragDrop objects
19470  * @param {object} config an object containing configurable attributes
19471  *                 Valid properties for DDTarget in addition to those in
19472  *                 DragDrop:
19473  *                    none
19474  */
19475 Roo.dd.DDTarget = function(id, sGroup, config) {
19476     if (id) {
19477         this.initTarget(id, sGroup, config);
19478     }
19479     if (config.listeners || config.events) { 
19480        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19481             listeners : config.listeners || {}, 
19482             events : config.events || {} 
19483         });    
19484     }
19485 };
19486
19487 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19488 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19489     toString: function() {
19490         return ("DDTarget " + this.id);
19491     }
19492 });
19493 /*
19494  * Based on:
19495  * Ext JS Library 1.1.1
19496  * Copyright(c) 2006-2007, Ext JS, LLC.
19497  *
19498  * Originally Released Under LGPL - original licence link has changed is not relivant.
19499  *
19500  * Fork - LGPL
19501  * <script type="text/javascript">
19502  */
19503  
19504
19505 /**
19506  * @class Roo.dd.ScrollManager
19507  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19508  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19509  * @singleton
19510  */
19511 Roo.dd.ScrollManager = function(){
19512     var ddm = Roo.dd.DragDropMgr;
19513     var els = {};
19514     var dragEl = null;
19515     var proc = {};
19516     
19517     
19518     
19519     var onStop = function(e){
19520         dragEl = null;
19521         clearProc();
19522     };
19523     
19524     var triggerRefresh = function(){
19525         if(ddm.dragCurrent){
19526              ddm.refreshCache(ddm.dragCurrent.groups);
19527         }
19528     };
19529     
19530     var doScroll = function(){
19531         if(ddm.dragCurrent){
19532             var dds = Roo.dd.ScrollManager;
19533             if(!dds.animate){
19534                 if(proc.el.scroll(proc.dir, dds.increment)){
19535                     triggerRefresh();
19536                 }
19537             }else{
19538                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19539             }
19540         }
19541     };
19542     
19543     var clearProc = function(){
19544         if(proc.id){
19545             clearInterval(proc.id);
19546         }
19547         proc.id = 0;
19548         proc.el = null;
19549         proc.dir = "";
19550     };
19551     
19552     var startProc = function(el, dir){
19553          Roo.log('scroll startproc');
19554         clearProc();
19555         proc.el = el;
19556         proc.dir = dir;
19557         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19558     };
19559     
19560     var onFire = function(e, isDrop){
19561        
19562         if(isDrop || !ddm.dragCurrent){ return; }
19563         var dds = Roo.dd.ScrollManager;
19564         if(!dragEl || dragEl != ddm.dragCurrent){
19565             dragEl = ddm.dragCurrent;
19566             // refresh regions on drag start
19567             dds.refreshCache();
19568         }
19569         
19570         var xy = Roo.lib.Event.getXY(e);
19571         var pt = new Roo.lib.Point(xy[0], xy[1]);
19572         for(var id in els){
19573             var el = els[id], r = el._region;
19574             if(r && r.contains(pt) && el.isScrollable()){
19575                 if(r.bottom - pt.y <= dds.thresh){
19576                     if(proc.el != el){
19577                         startProc(el, "down");
19578                     }
19579                     return;
19580                 }else if(r.right - pt.x <= dds.thresh){
19581                     if(proc.el != el){
19582                         startProc(el, "left");
19583                     }
19584                     return;
19585                 }else if(pt.y - r.top <= dds.thresh){
19586                     if(proc.el != el){
19587                         startProc(el, "up");
19588                     }
19589                     return;
19590                 }else if(pt.x - r.left <= dds.thresh){
19591                     if(proc.el != el){
19592                         startProc(el, "right");
19593                     }
19594                     return;
19595                 }
19596             }
19597         }
19598         clearProc();
19599     };
19600     
19601     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19602     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19603     
19604     return {
19605         /**
19606          * Registers new overflow element(s) to auto scroll
19607          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19608          */
19609         register : function(el){
19610             if(el instanceof Array){
19611                 for(var i = 0, len = el.length; i < len; i++) {
19612                         this.register(el[i]);
19613                 }
19614             }else{
19615                 el = Roo.get(el);
19616                 els[el.id] = el;
19617             }
19618             Roo.dd.ScrollManager.els = els;
19619         },
19620         
19621         /**
19622          * Unregisters overflow element(s) so they are no longer scrolled
19623          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19624          */
19625         unregister : function(el){
19626             if(el instanceof Array){
19627                 for(var i = 0, len = el.length; i < len; i++) {
19628                         this.unregister(el[i]);
19629                 }
19630             }else{
19631                 el = Roo.get(el);
19632                 delete els[el.id];
19633             }
19634         },
19635         
19636         /**
19637          * The number of pixels from the edge of a container the pointer needs to be to 
19638          * trigger scrolling (defaults to 25)
19639          * @type Number
19640          */
19641         thresh : 25,
19642         
19643         /**
19644          * The number of pixels to scroll in each scroll increment (defaults to 50)
19645          * @type Number
19646          */
19647         increment : 100,
19648         
19649         /**
19650          * The frequency of scrolls in milliseconds (defaults to 500)
19651          * @type Number
19652          */
19653         frequency : 500,
19654         
19655         /**
19656          * True to animate the scroll (defaults to true)
19657          * @type Boolean
19658          */
19659         animate: true,
19660         
19661         /**
19662          * The animation duration in seconds - 
19663          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19664          * @type Number
19665          */
19666         animDuration: .4,
19667         
19668         /**
19669          * Manually trigger a cache refresh.
19670          */
19671         refreshCache : function(){
19672             for(var id in els){
19673                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19674                     els[id]._region = els[id].getRegion();
19675                 }
19676             }
19677         }
19678     };
19679 }();/*
19680  * Based on:
19681  * Ext JS Library 1.1.1
19682  * Copyright(c) 2006-2007, Ext JS, LLC.
19683  *
19684  * Originally Released Under LGPL - original licence link has changed is not relivant.
19685  *
19686  * Fork - LGPL
19687  * <script type="text/javascript">
19688  */
19689  
19690
19691 /**
19692  * @class Roo.dd.Registry
19693  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19694  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19695  * @singleton
19696  */
19697 Roo.dd.Registry = function(){
19698     var elements = {}; 
19699     var handles = {}; 
19700     var autoIdSeed = 0;
19701
19702     var getId = function(el, autogen){
19703         if(typeof el == "string"){
19704             return el;
19705         }
19706         var id = el.id;
19707         if(!id && autogen !== false){
19708             id = "roodd-" + (++autoIdSeed);
19709             el.id = id;
19710         }
19711         return id;
19712     };
19713     
19714     return {
19715     /**
19716      * Register a drag drop element
19717      * @param {String|HTMLElement} element The id or DOM node to register
19718      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19719      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19720      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19721      * populated in the data object (if applicable):
19722      * <pre>
19723 Value      Description<br />
19724 ---------  ------------------------------------------<br />
19725 handles    Array of DOM nodes that trigger dragging<br />
19726            for the element being registered<br />
19727 isHandle   True if the element passed in triggers<br />
19728            dragging itself, else false
19729 </pre>
19730      */
19731         register : function(el, data){
19732             data = data || {};
19733             if(typeof el == "string"){
19734                 el = document.getElementById(el);
19735             }
19736             data.ddel = el;
19737             elements[getId(el)] = data;
19738             if(data.isHandle !== false){
19739                 handles[data.ddel.id] = data;
19740             }
19741             if(data.handles){
19742                 var hs = data.handles;
19743                 for(var i = 0, len = hs.length; i < len; i++){
19744                         handles[getId(hs[i])] = data;
19745                 }
19746             }
19747         },
19748
19749     /**
19750      * Unregister a drag drop element
19751      * @param {String|HTMLElement}  element The id or DOM node to unregister
19752      */
19753         unregister : function(el){
19754             var id = getId(el, false);
19755             var data = elements[id];
19756             if(data){
19757                 delete elements[id];
19758                 if(data.handles){
19759                     var hs = data.handles;
19760                     for(var i = 0, len = hs.length; i < len; i++){
19761                         delete handles[getId(hs[i], false)];
19762                     }
19763                 }
19764             }
19765         },
19766
19767     /**
19768      * Returns the handle registered for a DOM Node by id
19769      * @param {String|HTMLElement} id The DOM node or id to look up
19770      * @return {Object} handle The custom handle data
19771      */
19772         getHandle : function(id){
19773             if(typeof id != "string"){ // must be element?
19774                 id = id.id;
19775             }
19776             return handles[id];
19777         },
19778
19779     /**
19780      * Returns the handle that is registered for the DOM node that is the target of the event
19781      * @param {Event} e The event
19782      * @return {Object} handle The custom handle data
19783      */
19784         getHandleFromEvent : function(e){
19785             var t = Roo.lib.Event.getTarget(e);
19786             return t ? handles[t.id] : null;
19787         },
19788
19789     /**
19790      * Returns a custom data object that is registered for a DOM node by id
19791      * @param {String|HTMLElement} id The DOM node or id to look up
19792      * @return {Object} data The custom data
19793      */
19794         getTarget : function(id){
19795             if(typeof id != "string"){ // must be element?
19796                 id = id.id;
19797             }
19798             return elements[id];
19799         },
19800
19801     /**
19802      * Returns a custom data object that is registered for the DOM node that is the target of the event
19803      * @param {Event} e The event
19804      * @return {Object} data The custom data
19805      */
19806         getTargetFromEvent : function(e){
19807             var t = Roo.lib.Event.getTarget(e);
19808             return t ? elements[t.id] || handles[t.id] : null;
19809         }
19810     };
19811 }();/*
19812  * Based on:
19813  * Ext JS Library 1.1.1
19814  * Copyright(c) 2006-2007, Ext JS, LLC.
19815  *
19816  * Originally Released Under LGPL - original licence link has changed is not relivant.
19817  *
19818  * Fork - LGPL
19819  * <script type="text/javascript">
19820  */
19821  
19822
19823 /**
19824  * @class Roo.dd.StatusProxy
19825  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19826  * default drag proxy used by all Roo.dd components.
19827  * @constructor
19828  * @param {Object} config
19829  */
19830 Roo.dd.StatusProxy = function(config){
19831     Roo.apply(this, config);
19832     this.id = this.id || Roo.id();
19833     this.el = new Roo.Layer({
19834         dh: {
19835             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19836                 {tag: "div", cls: "x-dd-drop-icon"},
19837                 {tag: "div", cls: "x-dd-drag-ghost"}
19838             ]
19839         }, 
19840         shadow: !config || config.shadow !== false
19841     });
19842     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19843     this.dropStatus = this.dropNotAllowed;
19844 };
19845
19846 Roo.dd.StatusProxy.prototype = {
19847     /**
19848      * @cfg {String} dropAllowed
19849      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19850      */
19851     dropAllowed : "x-dd-drop-ok",
19852     /**
19853      * @cfg {String} dropNotAllowed
19854      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19855      */
19856     dropNotAllowed : "x-dd-drop-nodrop",
19857
19858     /**
19859      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19860      * over the current target element.
19861      * @param {String} cssClass The css class for the new drop status indicator image
19862      */
19863     setStatus : function(cssClass){
19864         cssClass = cssClass || this.dropNotAllowed;
19865         if(this.dropStatus != cssClass){
19866             this.el.replaceClass(this.dropStatus, cssClass);
19867             this.dropStatus = cssClass;
19868         }
19869     },
19870
19871     /**
19872      * Resets the status indicator to the default dropNotAllowed value
19873      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19874      */
19875     reset : function(clearGhost){
19876         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19877         this.dropStatus = this.dropNotAllowed;
19878         if(clearGhost){
19879             this.ghost.update("");
19880         }
19881     },
19882
19883     /**
19884      * Updates the contents of the ghost element
19885      * @param {String} html The html that will replace the current innerHTML of the ghost element
19886      */
19887     update : function(html){
19888         if(typeof html == "string"){
19889             this.ghost.update(html);
19890         }else{
19891             this.ghost.update("");
19892             html.style.margin = "0";
19893             this.ghost.dom.appendChild(html);
19894         }
19895         // ensure float = none set?? cant remember why though.
19896         var el = this.ghost.dom.firstChild;
19897                 if(el){
19898                         Roo.fly(el).setStyle('float', 'none');
19899                 }
19900     },
19901     
19902     /**
19903      * Returns the underlying proxy {@link Roo.Layer}
19904      * @return {Roo.Layer} el
19905     */
19906     getEl : function(){
19907         return this.el;
19908     },
19909
19910     /**
19911      * Returns the ghost element
19912      * @return {Roo.Element} el
19913      */
19914     getGhost : function(){
19915         return this.ghost;
19916     },
19917
19918     /**
19919      * Hides the proxy
19920      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19921      */
19922     hide : function(clear){
19923         this.el.hide();
19924         if(clear){
19925             this.reset(true);
19926         }
19927     },
19928
19929     /**
19930      * Stops the repair animation if it's currently running
19931      */
19932     stop : function(){
19933         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19934             this.anim.stop();
19935         }
19936     },
19937
19938     /**
19939      * Displays this proxy
19940      */
19941     show : function(){
19942         this.el.show();
19943     },
19944
19945     /**
19946      * Force the Layer to sync its shadow and shim positions to the element
19947      */
19948     sync : function(){
19949         this.el.sync();
19950     },
19951
19952     /**
19953      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19954      * invalid drop operation by the item being dragged.
19955      * @param {Array} xy The XY position of the element ([x, y])
19956      * @param {Function} callback The function to call after the repair is complete
19957      * @param {Object} scope The scope in which to execute the callback
19958      */
19959     repair : function(xy, callback, scope){
19960         this.callback = callback;
19961         this.scope = scope;
19962         if(xy && this.animRepair !== false){
19963             this.el.addClass("x-dd-drag-repair");
19964             this.el.hideUnders(true);
19965             this.anim = this.el.shift({
19966                 duration: this.repairDuration || .5,
19967                 easing: 'easeOut',
19968                 xy: xy,
19969                 stopFx: true,
19970                 callback: this.afterRepair,
19971                 scope: this
19972             });
19973         }else{
19974             this.afterRepair();
19975         }
19976     },
19977
19978     // private
19979     afterRepair : function(){
19980         this.hide(true);
19981         if(typeof this.callback == "function"){
19982             this.callback.call(this.scope || this);
19983         }
19984         this.callback = null;
19985         this.scope = null;
19986     }
19987 };/*
19988  * Based on:
19989  * Ext JS Library 1.1.1
19990  * Copyright(c) 2006-2007, Ext JS, LLC.
19991  *
19992  * Originally Released Under LGPL - original licence link has changed is not relivant.
19993  *
19994  * Fork - LGPL
19995  * <script type="text/javascript">
19996  */
19997
19998 /**
19999  * @class Roo.dd.DragSource
20000  * @extends Roo.dd.DDProxy
20001  * A simple class that provides the basic implementation needed to make any element draggable.
20002  * @constructor
20003  * @param {String/HTMLElement/Element} el The container element
20004  * @param {Object} config
20005  */
20006 Roo.dd.DragSource = function(el, config){
20007     this.el = Roo.get(el);
20008     this.dragData = {};
20009     
20010     Roo.apply(this, config);
20011     
20012     if(!this.proxy){
20013         this.proxy = new Roo.dd.StatusProxy();
20014     }
20015
20016     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20017           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20018     
20019     this.dragging = false;
20020 };
20021
20022 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20023     /**
20024      * @cfg {String} dropAllowed
20025      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20026      */
20027     dropAllowed : "x-dd-drop-ok",
20028     /**
20029      * @cfg {String} dropNotAllowed
20030      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20031      */
20032     dropNotAllowed : "x-dd-drop-nodrop",
20033
20034     /**
20035      * Returns the data object associated with this drag source
20036      * @return {Object} data An object containing arbitrary data
20037      */
20038     getDragData : function(e){
20039         return this.dragData;
20040     },
20041
20042     // private
20043     onDragEnter : function(e, id){
20044         var target = Roo.dd.DragDropMgr.getDDById(id);
20045         this.cachedTarget = target;
20046         if(this.beforeDragEnter(target, e, id) !== false){
20047             if(target.isNotifyTarget){
20048                 var status = target.notifyEnter(this, e, this.dragData);
20049                 this.proxy.setStatus(status);
20050             }else{
20051                 this.proxy.setStatus(this.dropAllowed);
20052             }
20053             
20054             if(this.afterDragEnter){
20055                 /**
20056                  * An empty function by default, but provided so that you can perform a custom action
20057                  * when the dragged item enters the drop target by providing an implementation.
20058                  * @param {Roo.dd.DragDrop} target The drop target
20059                  * @param {Event} e The event object
20060                  * @param {String} id The id of the dragged element
20061                  * @method afterDragEnter
20062                  */
20063                 this.afterDragEnter(target, e, id);
20064             }
20065         }
20066     },
20067
20068     /**
20069      * An empty function by default, but provided so that you can perform a custom action
20070      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20071      * @param {Roo.dd.DragDrop} target The drop target
20072      * @param {Event} e The event object
20073      * @param {String} id The id of the dragged element
20074      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20075      */
20076     beforeDragEnter : function(target, e, id){
20077         return true;
20078     },
20079
20080     // private
20081     alignElWithMouse: function() {
20082         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20083         this.proxy.sync();
20084     },
20085
20086     // private
20087     onDragOver : function(e, id){
20088         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20089         if(this.beforeDragOver(target, e, id) !== false){
20090             if(target.isNotifyTarget){
20091                 var status = target.notifyOver(this, e, this.dragData);
20092                 this.proxy.setStatus(status);
20093             }
20094
20095             if(this.afterDragOver){
20096                 /**
20097                  * An empty function by default, but provided so that you can perform a custom action
20098                  * while the dragged item is over the drop target by providing an implementation.
20099                  * @param {Roo.dd.DragDrop} target The drop target
20100                  * @param {Event} e The event object
20101                  * @param {String} id The id of the dragged element
20102                  * @method afterDragOver
20103                  */
20104                 this.afterDragOver(target, e, id);
20105             }
20106         }
20107     },
20108
20109     /**
20110      * An empty function by default, but provided so that you can perform a custom action
20111      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20112      * @param {Roo.dd.DragDrop} target The drop target
20113      * @param {Event} e The event object
20114      * @param {String} id The id of the dragged element
20115      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20116      */
20117     beforeDragOver : function(target, e, id){
20118         return true;
20119     },
20120
20121     // private
20122     onDragOut : function(e, id){
20123         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20124         if(this.beforeDragOut(target, e, id) !== false){
20125             if(target.isNotifyTarget){
20126                 target.notifyOut(this, e, this.dragData);
20127             }
20128             this.proxy.reset();
20129             if(this.afterDragOut){
20130                 /**
20131                  * An empty function by default, but provided so that you can perform a custom action
20132                  * after the dragged item is dragged out of the target without dropping.
20133                  * @param {Roo.dd.DragDrop} target The drop target
20134                  * @param {Event} e The event object
20135                  * @param {String} id The id of the dragged element
20136                  * @method afterDragOut
20137                  */
20138                 this.afterDragOut(target, e, id);
20139             }
20140         }
20141         this.cachedTarget = null;
20142     },
20143
20144     /**
20145      * An empty function by default, but provided so that you can perform a custom action before the dragged
20146      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20147      * @param {Roo.dd.DragDrop} target The drop target
20148      * @param {Event} e The event object
20149      * @param {String} id The id of the dragged element
20150      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20151      */
20152     beforeDragOut : function(target, e, id){
20153         return true;
20154     },
20155     
20156     // private
20157     onDragDrop : function(e, id){
20158         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20159         if(this.beforeDragDrop(target, e, id) !== false){
20160             if(target.isNotifyTarget){
20161                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20162                     this.onValidDrop(target, e, id);
20163                 }else{
20164                     this.onInvalidDrop(target, e, id);
20165                 }
20166             }else{
20167                 this.onValidDrop(target, e, id);
20168             }
20169             
20170             if(this.afterDragDrop){
20171                 /**
20172                  * An empty function by default, but provided so that you can perform a custom action
20173                  * after a valid drag drop has occurred by providing an implementation.
20174                  * @param {Roo.dd.DragDrop} target The drop target
20175                  * @param {Event} e The event object
20176                  * @param {String} id The id of the dropped element
20177                  * @method afterDragDrop
20178                  */
20179                 this.afterDragDrop(target, e, id);
20180             }
20181         }
20182         delete this.cachedTarget;
20183     },
20184
20185     /**
20186      * An empty function by default, but provided so that you can perform a custom action before the dragged
20187      * item is dropped onto the target and optionally cancel the onDragDrop.
20188      * @param {Roo.dd.DragDrop} target The drop target
20189      * @param {Event} e The event object
20190      * @param {String} id The id of the dragged element
20191      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20192      */
20193     beforeDragDrop : function(target, e, id){
20194         return true;
20195     },
20196
20197     // private
20198     onValidDrop : function(target, e, id){
20199         this.hideProxy();
20200         if(this.afterValidDrop){
20201             /**
20202              * An empty function by default, but provided so that you can perform a custom action
20203              * after a valid drop has occurred by providing an implementation.
20204              * @param {Object} target The target DD 
20205              * @param {Event} e The event object
20206              * @param {String} id The id of the dropped element
20207              * @method afterInvalidDrop
20208              */
20209             this.afterValidDrop(target, e, id);
20210         }
20211     },
20212
20213     // private
20214     getRepairXY : function(e, data){
20215         return this.el.getXY();  
20216     },
20217
20218     // private
20219     onInvalidDrop : function(target, e, id){
20220         this.beforeInvalidDrop(target, e, id);
20221         if(this.cachedTarget){
20222             if(this.cachedTarget.isNotifyTarget){
20223                 this.cachedTarget.notifyOut(this, e, this.dragData);
20224             }
20225             this.cacheTarget = null;
20226         }
20227         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20228
20229         if(this.afterInvalidDrop){
20230             /**
20231              * An empty function by default, but provided so that you can perform a custom action
20232              * after an invalid drop has occurred by providing an implementation.
20233              * @param {Event} e The event object
20234              * @param {String} id The id of the dropped element
20235              * @method afterInvalidDrop
20236              */
20237             this.afterInvalidDrop(e, id);
20238         }
20239     },
20240
20241     // private
20242     afterRepair : function(){
20243         if(Roo.enableFx){
20244             this.el.highlight(this.hlColor || "c3daf9");
20245         }
20246         this.dragging = false;
20247     },
20248
20249     /**
20250      * An empty function by default, but provided so that you can perform a custom action after an invalid
20251      * drop has occurred.
20252      * @param {Roo.dd.DragDrop} target The drop target
20253      * @param {Event} e The event object
20254      * @param {String} id The id of the dragged element
20255      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20256      */
20257     beforeInvalidDrop : function(target, e, id){
20258         return true;
20259     },
20260
20261     // private
20262     handleMouseDown : function(e){
20263         if(this.dragging) {
20264             return;
20265         }
20266         var data = this.getDragData(e);
20267         if(data && this.onBeforeDrag(data, e) !== false){
20268             this.dragData = data;
20269             this.proxy.stop();
20270             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20271         } 
20272     },
20273
20274     /**
20275      * An empty function by default, but provided so that you can perform a custom action before the initial
20276      * drag event begins and optionally cancel it.
20277      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20278      * @param {Event} e The event object
20279      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20280      */
20281     onBeforeDrag : function(data, e){
20282         return true;
20283     },
20284
20285     /**
20286      * An empty function by default, but provided so that you can perform a custom action once the initial
20287      * drag event has begun.  The drag cannot be canceled from this function.
20288      * @param {Number} x The x position of the click on the dragged object
20289      * @param {Number} y The y position of the click on the dragged object
20290      */
20291     onStartDrag : Roo.emptyFn,
20292
20293     // private - YUI override
20294     startDrag : function(x, y){
20295         this.proxy.reset();
20296         this.dragging = true;
20297         this.proxy.update("");
20298         this.onInitDrag(x, y);
20299         this.proxy.show();
20300     },
20301
20302     // private
20303     onInitDrag : function(x, y){
20304         var clone = this.el.dom.cloneNode(true);
20305         clone.id = Roo.id(); // prevent duplicate ids
20306         this.proxy.update(clone);
20307         this.onStartDrag(x, y);
20308         return true;
20309     },
20310
20311     /**
20312      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20313      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20314      */
20315     getProxy : function(){
20316         return this.proxy;  
20317     },
20318
20319     /**
20320      * Hides the drag source's {@link Roo.dd.StatusProxy}
20321      */
20322     hideProxy : function(){
20323         this.proxy.hide();  
20324         this.proxy.reset(true);
20325         this.dragging = false;
20326     },
20327
20328     // private
20329     triggerCacheRefresh : function(){
20330         Roo.dd.DDM.refreshCache(this.groups);
20331     },
20332
20333     // private - override to prevent hiding
20334     b4EndDrag: function(e) {
20335     },
20336
20337     // private - override to prevent moving
20338     endDrag : function(e){
20339         this.onEndDrag(this.dragData, e);
20340     },
20341
20342     // private
20343     onEndDrag : function(data, e){
20344     },
20345     
20346     // private - pin to cursor
20347     autoOffset : function(x, y) {
20348         this.setDelta(-12, -20);
20349     }    
20350 });/*
20351  * Based on:
20352  * Ext JS Library 1.1.1
20353  * Copyright(c) 2006-2007, Ext JS, LLC.
20354  *
20355  * Originally Released Under LGPL - original licence link has changed is not relivant.
20356  *
20357  * Fork - LGPL
20358  * <script type="text/javascript">
20359  */
20360
20361
20362 /**
20363  * @class Roo.dd.DropTarget
20364  * @extends Roo.dd.DDTarget
20365  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20366  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20367  * @constructor
20368  * @param {String/HTMLElement/Element} el The container element
20369  * @param {Object} config
20370  */
20371 Roo.dd.DropTarget = function(el, config){
20372     this.el = Roo.get(el);
20373     
20374     var listeners = false; ;
20375     if (config && config.listeners) {
20376         listeners= config.listeners;
20377         delete config.listeners;
20378     }
20379     Roo.apply(this, config);
20380     
20381     if(this.containerScroll){
20382         Roo.dd.ScrollManager.register(this.el);
20383     }
20384     this.addEvents( {
20385          /**
20386          * @scope Roo.dd.DropTarget
20387          */
20388          
20389          /**
20390          * @event enter
20391          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20392          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20393          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20394          * 
20395          * IMPORTANT : it should set this.overClass and this.dropAllowed
20396          * 
20397          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20398          * @param {Event} e The event
20399          * @param {Object} data An object containing arbitrary data supplied by the drag source
20400          */
20401         "enter" : true,
20402         
20403          /**
20404          * @event over
20405          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20406          * This method will be called on every mouse movement while the drag source is over the drop target.
20407          * This default implementation simply returns the dropAllowed config value.
20408          * 
20409          * IMPORTANT : it should set this.dropAllowed
20410          * 
20411          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20412          * @param {Event} e The event
20413          * @param {Object} data An object containing arbitrary data supplied by the drag source
20414          
20415          */
20416         "over" : true,
20417         /**
20418          * @event out
20419          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20420          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20421          * overClass (if any) from the drop element.
20422          * 
20423          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20424          * @param {Event} e The event
20425          * @param {Object} data An object containing arbitrary data supplied by the drag source
20426          */
20427          "out" : true,
20428          
20429         /**
20430          * @event drop
20431          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20432          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20433          * implementation that does something to process the drop event and returns true so that the drag source's
20434          * repair action does not run.
20435          * 
20436          * IMPORTANT : it should set this.success
20437          * 
20438          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20439          * @param {Event} e The event
20440          * @param {Object} data An object containing arbitrary data supplied by the drag source
20441         */
20442          "drop" : true
20443     });
20444             
20445      
20446     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20447         this.el.dom, 
20448         this.ddGroup || this.group,
20449         {
20450             isTarget: true,
20451             listeners : listeners || {} 
20452            
20453         
20454         }
20455     );
20456
20457 };
20458
20459 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20460     /**
20461      * @cfg {String} overClass
20462      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20463      */
20464      /**
20465      * @cfg {String} ddGroup
20466      * The drag drop group to handle drop events for
20467      */
20468      
20469     /**
20470      * @cfg {String} dropAllowed
20471      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20472      */
20473     dropAllowed : "x-dd-drop-ok",
20474     /**
20475      * @cfg {String} dropNotAllowed
20476      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20477      */
20478     dropNotAllowed : "x-dd-drop-nodrop",
20479     /**
20480      * @cfg {boolean} success
20481      * set this after drop listener.. 
20482      */
20483     success : false,
20484     /**
20485      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20486      * if the drop point is valid for over/enter..
20487      */
20488     valid : false,
20489     // private
20490     isTarget : true,
20491
20492     // private
20493     isNotifyTarget : true,
20494     
20495     /**
20496      * @hide
20497      */
20498     notifyEnter : function(dd, e, data)
20499     {
20500         this.valid = true;
20501         this.fireEvent('enter', dd, e, data);
20502         if(this.overClass){
20503             this.el.addClass(this.overClass);
20504         }
20505         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20506             this.valid ? this.dropAllowed : this.dropNotAllowed
20507         );
20508     },
20509
20510     /**
20511      * @hide
20512      */
20513     notifyOver : function(dd, e, data)
20514     {
20515         this.valid = true;
20516         this.fireEvent('over', dd, e, data);
20517         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20518             this.valid ? this.dropAllowed : this.dropNotAllowed
20519         );
20520     },
20521
20522     /**
20523      * @hide
20524      */
20525     notifyOut : function(dd, e, data)
20526     {
20527         this.fireEvent('out', dd, e, data);
20528         if(this.overClass){
20529             this.el.removeClass(this.overClass);
20530         }
20531     },
20532
20533     /**
20534      * @hide
20535      */
20536     notifyDrop : function(dd, e, data)
20537     {
20538         this.success = false;
20539         this.fireEvent('drop', dd, e, data);
20540         return this.success;
20541     }
20542 });/*
20543  * Based on:
20544  * Ext JS Library 1.1.1
20545  * Copyright(c) 2006-2007, Ext JS, LLC.
20546  *
20547  * Originally Released Under LGPL - original licence link has changed is not relivant.
20548  *
20549  * Fork - LGPL
20550  * <script type="text/javascript">
20551  */
20552
20553
20554 /**
20555  * @class Roo.dd.DragZone
20556  * @extends Roo.dd.DragSource
20557  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20558  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20559  * @constructor
20560  * @param {String/HTMLElement/Element} el The container element
20561  * @param {Object} config
20562  */
20563 Roo.dd.DragZone = function(el, config){
20564     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20565     if(this.containerScroll){
20566         Roo.dd.ScrollManager.register(this.el);
20567     }
20568 };
20569
20570 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20571     /**
20572      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20573      * for auto scrolling during drag operations.
20574      */
20575     /**
20576      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20577      * method after a failed drop (defaults to "c3daf9" - light blue)
20578      */
20579
20580     /**
20581      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20582      * for a valid target to drag based on the mouse down. Override this method
20583      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20584      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20585      * @param {EventObject} e The mouse down event
20586      * @return {Object} The dragData
20587      */
20588     getDragData : function(e){
20589         return Roo.dd.Registry.getHandleFromEvent(e);
20590     },
20591     
20592     /**
20593      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20594      * this.dragData.ddel
20595      * @param {Number} x The x position of the click on the dragged object
20596      * @param {Number} y The y position of the click on the dragged object
20597      * @return {Boolean} true to continue the drag, false to cancel
20598      */
20599     onInitDrag : function(x, y){
20600         this.proxy.update(this.dragData.ddel.cloneNode(true));
20601         this.onStartDrag(x, y);
20602         return true;
20603     },
20604     
20605     /**
20606      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20607      */
20608     afterRepair : function(){
20609         if(Roo.enableFx){
20610             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20611         }
20612         this.dragging = false;
20613     },
20614
20615     /**
20616      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20617      * the XY of this.dragData.ddel
20618      * @param {EventObject} e The mouse up event
20619      * @return {Array} The xy location (e.g. [100, 200])
20620      */
20621     getRepairXY : function(e){
20622         return Roo.Element.fly(this.dragData.ddel).getXY();  
20623     }
20624 });/*
20625  * Based on:
20626  * Ext JS Library 1.1.1
20627  * Copyright(c) 2006-2007, Ext JS, LLC.
20628  *
20629  * Originally Released Under LGPL - original licence link has changed is not relivant.
20630  *
20631  * Fork - LGPL
20632  * <script type="text/javascript">
20633  */
20634 /**
20635  * @class Roo.dd.DropZone
20636  * @extends Roo.dd.DropTarget
20637  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20638  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20639  * @constructor
20640  * @param {String/HTMLElement/Element} el The container element
20641  * @param {Object} config
20642  */
20643 Roo.dd.DropZone = function(el, config){
20644     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20645 };
20646
20647 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20648     /**
20649      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20650      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20651      * provide your own custom lookup.
20652      * @param {Event} e The event
20653      * @return {Object} data The custom data
20654      */
20655     getTargetFromEvent : function(e){
20656         return Roo.dd.Registry.getTargetFromEvent(e);
20657     },
20658
20659     /**
20660      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20661      * that it has registered.  This method has no default implementation and should be overridden to provide
20662      * node-specific processing if necessary.
20663      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20664      * {@link #getTargetFromEvent} for this node)
20665      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20666      * @param {Event} e The event
20667      * @param {Object} data An object containing arbitrary data supplied by the drag source
20668      */
20669     onNodeEnter : function(n, dd, e, data){
20670         
20671     },
20672
20673     /**
20674      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20675      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20676      * overridden to provide the proper feedback.
20677      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20678      * {@link #getTargetFromEvent} for this node)
20679      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20680      * @param {Event} e The event
20681      * @param {Object} data An object containing arbitrary data supplied by the drag source
20682      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20683      * underlying {@link Roo.dd.StatusProxy} can be updated
20684      */
20685     onNodeOver : function(n, dd, e, data){
20686         return this.dropAllowed;
20687     },
20688
20689     /**
20690      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20691      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20692      * node-specific processing if necessary.
20693      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20694      * {@link #getTargetFromEvent} for this node)
20695      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20696      * @param {Event} e The event
20697      * @param {Object} data An object containing arbitrary data supplied by the drag source
20698      */
20699     onNodeOut : function(n, dd, e, data){
20700         
20701     },
20702
20703     /**
20704      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20705      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20706      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20707      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20708      * {@link #getTargetFromEvent} for this node)
20709      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20710      * @param {Event} e The event
20711      * @param {Object} data An object containing arbitrary data supplied by the drag source
20712      * @return {Boolean} True if the drop was valid, else false
20713      */
20714     onNodeDrop : function(n, dd, e, data){
20715         return false;
20716     },
20717
20718     /**
20719      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20720      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20721      * it should be overridden to provide the proper feedback if necessary.
20722      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20723      * @param {Event} e The event
20724      * @param {Object} data An object containing arbitrary data supplied by the drag source
20725      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20726      * underlying {@link Roo.dd.StatusProxy} can be updated
20727      */
20728     onContainerOver : function(dd, e, data){
20729         return this.dropNotAllowed;
20730     },
20731
20732     /**
20733      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20734      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20735      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20736      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20737      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20738      * @param {Event} e The event
20739      * @param {Object} data An object containing arbitrary data supplied by the drag source
20740      * @return {Boolean} True if the drop was valid, else false
20741      */
20742     onContainerDrop : function(dd, e, data){
20743         return false;
20744     },
20745
20746     /**
20747      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20748      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20749      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20750      * you should override this method and provide a custom implementation.
20751      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20752      * @param {Event} e The event
20753      * @param {Object} data An object containing arbitrary data supplied by the drag source
20754      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20755      * underlying {@link Roo.dd.StatusProxy} can be updated
20756      */
20757     notifyEnter : function(dd, e, data){
20758         return this.dropNotAllowed;
20759     },
20760
20761     /**
20762      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20763      * This method will be called on every mouse movement while the drag source is over the drop zone.
20764      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20765      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20766      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20767      * registered node, it will call {@link #onContainerOver}.
20768      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20769      * @param {Event} e The event
20770      * @param {Object} data An object containing arbitrary data supplied by the drag source
20771      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20772      * underlying {@link Roo.dd.StatusProxy} can be updated
20773      */
20774     notifyOver : function(dd, e, data){
20775         var n = this.getTargetFromEvent(e);
20776         if(!n){ // not over valid drop target
20777             if(this.lastOverNode){
20778                 this.onNodeOut(this.lastOverNode, dd, e, data);
20779                 this.lastOverNode = null;
20780             }
20781             return this.onContainerOver(dd, e, data);
20782         }
20783         if(this.lastOverNode != n){
20784             if(this.lastOverNode){
20785                 this.onNodeOut(this.lastOverNode, dd, e, data);
20786             }
20787             this.onNodeEnter(n, dd, e, data);
20788             this.lastOverNode = n;
20789         }
20790         return this.onNodeOver(n, dd, e, data);
20791     },
20792
20793     /**
20794      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20795      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20796      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20797      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20798      * @param {Event} e The event
20799      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20800      */
20801     notifyOut : function(dd, e, data){
20802         if(this.lastOverNode){
20803             this.onNodeOut(this.lastOverNode, dd, e, data);
20804             this.lastOverNode = null;
20805         }
20806     },
20807
20808     /**
20809      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20810      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20811      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20812      * otherwise it will call {@link #onContainerDrop}.
20813      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20814      * @param {Event} e The event
20815      * @param {Object} data An object containing arbitrary data supplied by the drag source
20816      * @return {Boolean} True if the drop was valid, else false
20817      */
20818     notifyDrop : function(dd, e, data){
20819         if(this.lastOverNode){
20820             this.onNodeOut(this.lastOverNode, dd, e, data);
20821             this.lastOverNode = null;
20822         }
20823         var n = this.getTargetFromEvent(e);
20824         return n ?
20825             this.onNodeDrop(n, dd, e, data) :
20826             this.onContainerDrop(dd, e, data);
20827     },
20828
20829     // private
20830     triggerCacheRefresh : function(){
20831         Roo.dd.DDM.refreshCache(this.groups);
20832     }  
20833 });/*
20834  * Based on:
20835  * Ext JS Library 1.1.1
20836  * Copyright(c) 2006-2007, Ext JS, LLC.
20837  *
20838  * Originally Released Under LGPL - original licence link has changed is not relivant.
20839  *
20840  * Fork - LGPL
20841  * <script type="text/javascript">
20842  */
20843
20844
20845 /**
20846  * @class Roo.data.SortTypes
20847  * @singleton
20848  * Defines the default sorting (casting?) comparison functions used when sorting data.
20849  */
20850 Roo.data.SortTypes = {
20851     /**
20852      * Default sort that does nothing
20853      * @param {Mixed} s The value being converted
20854      * @return {Mixed} The comparison value
20855      */
20856     none : function(s){
20857         return s;
20858     },
20859     
20860     /**
20861      * The regular expression used to strip tags
20862      * @type {RegExp}
20863      * @property
20864      */
20865     stripTagsRE : /<\/?[^>]+>/gi,
20866     
20867     /**
20868      * Strips all HTML tags to sort on text only
20869      * @param {Mixed} s The value being converted
20870      * @return {String} The comparison value
20871      */
20872     asText : function(s){
20873         return String(s).replace(this.stripTagsRE, "");
20874     },
20875     
20876     /**
20877      * Strips all HTML tags to sort on text only - Case insensitive
20878      * @param {Mixed} s The value being converted
20879      * @return {String} The comparison value
20880      */
20881     asUCText : function(s){
20882         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20883     },
20884     
20885     /**
20886      * Case insensitive string
20887      * @param {Mixed} s The value being converted
20888      * @return {String} The comparison value
20889      */
20890     asUCString : function(s) {
20891         return String(s).toUpperCase();
20892     },
20893     
20894     /**
20895      * Date sorting
20896      * @param {Mixed} s The value being converted
20897      * @return {Number} The comparison value
20898      */
20899     asDate : function(s) {
20900         if(!s){
20901             return 0;
20902         }
20903         if(s instanceof Date){
20904             return s.getTime();
20905         }
20906         return Date.parse(String(s));
20907     },
20908     
20909     /**
20910      * Float sorting
20911      * @param {Mixed} s The value being converted
20912      * @return {Float} The comparison value
20913      */
20914     asFloat : function(s) {
20915         var val = parseFloat(String(s).replace(/,/g, ""));
20916         if(isNaN(val)) val = 0;
20917         return val;
20918     },
20919     
20920     /**
20921      * Integer sorting
20922      * @param {Mixed} s The value being converted
20923      * @return {Number} The comparison value
20924      */
20925     asInt : function(s) {
20926         var val = parseInt(String(s).replace(/,/g, ""));
20927         if(isNaN(val)) val = 0;
20928         return val;
20929     }
20930 };/*
20931  * Based on:
20932  * Ext JS Library 1.1.1
20933  * Copyright(c) 2006-2007, Ext JS, LLC.
20934  *
20935  * Originally Released Under LGPL - original licence link has changed is not relivant.
20936  *
20937  * Fork - LGPL
20938  * <script type="text/javascript">
20939  */
20940
20941 /**
20942 * @class Roo.data.Record
20943  * Instances of this class encapsulate both record <em>definition</em> information, and record
20944  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20945  * to access Records cached in an {@link Roo.data.Store} object.<br>
20946  * <p>
20947  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20948  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20949  * objects.<br>
20950  * <p>
20951  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20952  * @constructor
20953  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20954  * {@link #create}. The parameters are the same.
20955  * @param {Array} data An associative Array of data values keyed by the field name.
20956  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20957  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20958  * not specified an integer id is generated.
20959  */
20960 Roo.data.Record = function(data, id){
20961     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20962     this.data = data;
20963 };
20964
20965 /**
20966  * Generate a constructor for a specific record layout.
20967  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20968  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20969  * Each field definition object may contain the following properties: <ul>
20970  * <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,
20971  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20972  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20973  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20974  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20975  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20976  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20977  * this may be omitted.</p></li>
20978  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20979  * <ul><li>auto (Default, implies no conversion)</li>
20980  * <li>string</li>
20981  * <li>int</li>
20982  * <li>float</li>
20983  * <li>boolean</li>
20984  * <li>date</li></ul></p></li>
20985  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20986  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20987  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20988  * by the Reader into an object that will be stored in the Record. It is passed the
20989  * following parameters:<ul>
20990  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20991  * </ul></p></li>
20992  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20993  * </ul>
20994  * <br>usage:<br><pre><code>
20995 var TopicRecord = Roo.data.Record.create(
20996     {name: 'title', mapping: 'topic_title'},
20997     {name: 'author', mapping: 'username'},
20998     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20999     {name: 'lastPost', mapping: 'post_time', type: 'date'},
21000     {name: 'lastPoster', mapping: 'user2'},
21001     {name: 'excerpt', mapping: 'post_text'}
21002 );
21003
21004 var myNewRecord = new TopicRecord({
21005     title: 'Do my job please',
21006     author: 'noobie',
21007     totalPosts: 1,
21008     lastPost: new Date(),
21009     lastPoster: 'Animal',
21010     excerpt: 'No way dude!'
21011 });
21012 myStore.add(myNewRecord);
21013 </code></pre>
21014  * @method create
21015  * @static
21016  */
21017 Roo.data.Record.create = function(o){
21018     var f = function(){
21019         f.superclass.constructor.apply(this, arguments);
21020     };
21021     Roo.extend(f, Roo.data.Record);
21022     var p = f.prototype;
21023     p.fields = new Roo.util.MixedCollection(false, function(field){
21024         return field.name;
21025     });
21026     for(var i = 0, len = o.length; i < len; i++){
21027         p.fields.add(new Roo.data.Field(o[i]));
21028     }
21029     f.getField = function(name){
21030         return p.fields.get(name);  
21031     };
21032     return f;
21033 };
21034
21035 Roo.data.Record.AUTO_ID = 1000;
21036 Roo.data.Record.EDIT = 'edit';
21037 Roo.data.Record.REJECT = 'reject';
21038 Roo.data.Record.COMMIT = 'commit';
21039
21040 Roo.data.Record.prototype = {
21041     /**
21042      * Readonly flag - true if this record has been modified.
21043      * @type Boolean
21044      */
21045     dirty : false,
21046     editing : false,
21047     error: null,
21048     modified: null,
21049
21050     // private
21051     join : function(store){
21052         this.store = store;
21053     },
21054
21055     /**
21056      * Set the named field to the specified value.
21057      * @param {String} name The name of the field to set.
21058      * @param {Object} value The value to set the field to.
21059      */
21060     set : function(name, value){
21061         if(this.data[name] == value){
21062             return;
21063         }
21064         this.dirty = true;
21065         if(!this.modified){
21066             this.modified = {};
21067         }
21068         if(typeof this.modified[name] == 'undefined'){
21069             this.modified[name] = this.data[name];
21070         }
21071         this.data[name] = value;
21072         if(!this.editing && this.store){
21073             this.store.afterEdit(this);
21074         }       
21075     },
21076
21077     /**
21078      * Get the value of the named field.
21079      * @param {String} name The name of the field to get the value of.
21080      * @return {Object} The value of the field.
21081      */
21082     get : function(name){
21083         return this.data[name]; 
21084     },
21085
21086     // private
21087     beginEdit : function(){
21088         this.editing = true;
21089         this.modified = {}; 
21090     },
21091
21092     // private
21093     cancelEdit : function(){
21094         this.editing = false;
21095         delete this.modified;
21096     },
21097
21098     // private
21099     endEdit : function(){
21100         this.editing = false;
21101         if(this.dirty && this.store){
21102             this.store.afterEdit(this);
21103         }
21104     },
21105
21106     /**
21107      * Usually called by the {@link Roo.data.Store} which owns the Record.
21108      * Rejects all changes made to the Record since either creation, or the last commit operation.
21109      * Modified fields are reverted to their original values.
21110      * <p>
21111      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21112      * of reject operations.
21113      */
21114     reject : function(){
21115         var m = this.modified;
21116         for(var n in m){
21117             if(typeof m[n] != "function"){
21118                 this.data[n] = m[n];
21119             }
21120         }
21121         this.dirty = false;
21122         delete this.modified;
21123         this.editing = false;
21124         if(this.store){
21125             this.store.afterReject(this);
21126         }
21127     },
21128
21129     /**
21130      * Usually called by the {@link Roo.data.Store} which owns the Record.
21131      * Commits all changes made to the Record since either creation, or the last commit operation.
21132      * <p>
21133      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21134      * of commit operations.
21135      */
21136     commit : function(){
21137         this.dirty = false;
21138         delete this.modified;
21139         this.editing = false;
21140         if(this.store){
21141             this.store.afterCommit(this);
21142         }
21143     },
21144
21145     // private
21146     hasError : function(){
21147         return this.error != null;
21148     },
21149
21150     // private
21151     clearError : function(){
21152         this.error = null;
21153     },
21154
21155     /**
21156      * Creates a copy of this record.
21157      * @param {String} id (optional) A new record id if you don't want to use this record's id
21158      * @return {Record}
21159      */
21160     copy : function(newId) {
21161         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21162     }
21163 };/*
21164  * Based on:
21165  * Ext JS Library 1.1.1
21166  * Copyright(c) 2006-2007, Ext JS, LLC.
21167  *
21168  * Originally Released Under LGPL - original licence link has changed is not relivant.
21169  *
21170  * Fork - LGPL
21171  * <script type="text/javascript">
21172  */
21173
21174
21175
21176 /**
21177  * @class Roo.data.Store
21178  * @extends Roo.util.Observable
21179  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21180  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21181  * <p>
21182  * 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
21183  * has no knowledge of the format of the data returned by the Proxy.<br>
21184  * <p>
21185  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21186  * instances from the data object. These records are cached and made available through accessor functions.
21187  * @constructor
21188  * Creates a new Store.
21189  * @param {Object} config A config object containing the objects needed for the Store to access data,
21190  * and read the data into Records.
21191  */
21192 Roo.data.Store = function(config){
21193     this.data = new Roo.util.MixedCollection(false);
21194     this.data.getKey = function(o){
21195         return o.id;
21196     };
21197     this.baseParams = {};
21198     // private
21199     this.paramNames = {
21200         "start" : "start",
21201         "limit" : "limit",
21202         "sort" : "sort",
21203         "dir" : "dir",
21204         "multisort" : "_multisort"
21205     };
21206
21207     if(config && config.data){
21208         this.inlineData = config.data;
21209         delete config.data;
21210     }
21211
21212     Roo.apply(this, config);
21213     
21214     if(this.reader){ // reader passed
21215         this.reader = Roo.factory(this.reader, Roo.data);
21216         this.reader.xmodule = this.xmodule || false;
21217         if(!this.recordType){
21218             this.recordType = this.reader.recordType;
21219         }
21220         if(this.reader.onMetaChange){
21221             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21222         }
21223     }
21224
21225     if(this.recordType){
21226         this.fields = this.recordType.prototype.fields;
21227     }
21228     this.modified = [];
21229
21230     this.addEvents({
21231         /**
21232          * @event datachanged
21233          * Fires when the data cache has changed, and a widget which is using this Store
21234          * as a Record cache should refresh its view.
21235          * @param {Store} this
21236          */
21237         datachanged : true,
21238         /**
21239          * @event metachange
21240          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21241          * @param {Store} this
21242          * @param {Object} meta The JSON metadata
21243          */
21244         metachange : true,
21245         /**
21246          * @event add
21247          * Fires when Records have been added to the Store
21248          * @param {Store} this
21249          * @param {Roo.data.Record[]} records The array of Records added
21250          * @param {Number} index The index at which the record(s) were added
21251          */
21252         add : true,
21253         /**
21254          * @event remove
21255          * Fires when a Record has been removed from the Store
21256          * @param {Store} this
21257          * @param {Roo.data.Record} record The Record that was removed
21258          * @param {Number} index The index at which the record was removed
21259          */
21260         remove : true,
21261         /**
21262          * @event update
21263          * Fires when a Record has been updated
21264          * @param {Store} this
21265          * @param {Roo.data.Record} record The Record that was updated
21266          * @param {String} operation The update operation being performed.  Value may be one of:
21267          * <pre><code>
21268  Roo.data.Record.EDIT
21269  Roo.data.Record.REJECT
21270  Roo.data.Record.COMMIT
21271          * </code></pre>
21272          */
21273         update : true,
21274         /**
21275          * @event clear
21276          * Fires when the data cache has been cleared.
21277          * @param {Store} this
21278          */
21279         clear : true,
21280         /**
21281          * @event beforeload
21282          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21283          * the load action will be canceled.
21284          * @param {Store} this
21285          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21286          */
21287         beforeload : true,
21288         /**
21289          * @event beforeloadadd
21290          * Fires after a new set of Records has been loaded.
21291          * @param {Store} this
21292          * @param {Roo.data.Record[]} records The Records that were loaded
21293          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21294          */
21295         beforeloadadd : true,
21296         /**
21297          * @event load
21298          * Fires after a new set of Records has been loaded, before they are added to the store.
21299          * @param {Store} this
21300          * @param {Roo.data.Record[]} records The Records that were loaded
21301          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21302          * @params {Object} return from reader
21303          */
21304         load : true,
21305         /**
21306          * @event loadexception
21307          * Fires if an exception occurs in the Proxy during loading.
21308          * Called with the signature of the Proxy's "loadexception" event.
21309          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21310          * 
21311          * @param {Proxy} 
21312          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21313          * @param {Object} load options 
21314          * @param {Object} jsonData from your request (normally this contains the Exception)
21315          */
21316         loadexception : true
21317     });
21318     
21319     if(this.proxy){
21320         this.proxy = Roo.factory(this.proxy, Roo.data);
21321         this.proxy.xmodule = this.xmodule || false;
21322         this.relayEvents(this.proxy,  ["loadexception"]);
21323     }
21324     this.sortToggle = {};
21325     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21326
21327     Roo.data.Store.superclass.constructor.call(this);
21328
21329     if(this.inlineData){
21330         this.loadData(this.inlineData);
21331         delete this.inlineData;
21332     }
21333 };
21334
21335 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21336      /**
21337     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21338     * without a remote query - used by combo/forms at present.
21339     */
21340     
21341     /**
21342     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21343     */
21344     /**
21345     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21346     */
21347     /**
21348     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21349     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21350     */
21351     /**
21352     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21353     * on any HTTP request
21354     */
21355     /**
21356     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21357     */
21358     /**
21359     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21360     */
21361     multiSort: false,
21362     /**
21363     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21364     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21365     */
21366     remoteSort : false,
21367
21368     /**
21369     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21370      * loaded or when a record is removed. (defaults to false).
21371     */
21372     pruneModifiedRecords : false,
21373
21374     // private
21375     lastOptions : null,
21376
21377     /**
21378      * Add Records to the Store and fires the add event.
21379      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21380      */
21381     add : function(records){
21382         records = [].concat(records);
21383         for(var i = 0, len = records.length; i < len; i++){
21384             records[i].join(this);
21385         }
21386         var index = this.data.length;
21387         this.data.addAll(records);
21388         this.fireEvent("add", this, records, index);
21389     },
21390
21391     /**
21392      * Remove a Record from the Store and fires the remove event.
21393      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21394      */
21395     remove : function(record){
21396         var index = this.data.indexOf(record);
21397         this.data.removeAt(index);
21398         if(this.pruneModifiedRecords){
21399             this.modified.remove(record);
21400         }
21401         this.fireEvent("remove", this, record, index);
21402     },
21403
21404     /**
21405      * Remove all Records from the Store and fires the clear event.
21406      */
21407     removeAll : function(){
21408         this.data.clear();
21409         if(this.pruneModifiedRecords){
21410             this.modified = [];
21411         }
21412         this.fireEvent("clear", this);
21413     },
21414
21415     /**
21416      * Inserts Records to the Store at the given index and fires the add event.
21417      * @param {Number} index The start index at which to insert the passed Records.
21418      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21419      */
21420     insert : function(index, records){
21421         records = [].concat(records);
21422         for(var i = 0, len = records.length; i < len; i++){
21423             this.data.insert(index, records[i]);
21424             records[i].join(this);
21425         }
21426         this.fireEvent("add", this, records, index);
21427     },
21428
21429     /**
21430      * Get the index within the cache of the passed Record.
21431      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21432      * @return {Number} The index of the passed Record. Returns -1 if not found.
21433      */
21434     indexOf : function(record){
21435         return this.data.indexOf(record);
21436     },
21437
21438     /**
21439      * Get the index within the cache of the Record with the passed id.
21440      * @param {String} id The id of the Record to find.
21441      * @return {Number} The index of the Record. Returns -1 if not found.
21442      */
21443     indexOfId : function(id){
21444         return this.data.indexOfKey(id);
21445     },
21446
21447     /**
21448      * Get the Record with the specified id.
21449      * @param {String} id The id of the Record to find.
21450      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21451      */
21452     getById : function(id){
21453         return this.data.key(id);
21454     },
21455
21456     /**
21457      * Get the Record at the specified index.
21458      * @param {Number} index The index of the Record to find.
21459      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21460      */
21461     getAt : function(index){
21462         return this.data.itemAt(index);
21463     },
21464
21465     /**
21466      * Returns a range of Records between specified indices.
21467      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21468      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21469      * @return {Roo.data.Record[]} An array of Records
21470      */
21471     getRange : function(start, end){
21472         return this.data.getRange(start, end);
21473     },
21474
21475     // private
21476     storeOptions : function(o){
21477         o = Roo.apply({}, o);
21478         delete o.callback;
21479         delete o.scope;
21480         this.lastOptions = o;
21481     },
21482
21483     /**
21484      * Loads the Record cache from the configured Proxy using the configured Reader.
21485      * <p>
21486      * If using remote paging, then the first load call must specify the <em>start</em>
21487      * and <em>limit</em> properties in the options.params property to establish the initial
21488      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21489      * <p>
21490      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21491      * and this call will return before the new data has been loaded. Perform any post-processing
21492      * in a callback function, or in a "load" event handler.</strong>
21493      * <p>
21494      * @param {Object} options An object containing properties which control loading options:<ul>
21495      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21496      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21497      * passed the following arguments:<ul>
21498      * <li>r : Roo.data.Record[]</li>
21499      * <li>options: Options object from the load call</li>
21500      * <li>success: Boolean success indicator</li></ul></li>
21501      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21502      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21503      * </ul>
21504      */
21505     load : function(options){
21506         options = options || {};
21507         if(this.fireEvent("beforeload", this, options) !== false){
21508             this.storeOptions(options);
21509             var p = Roo.apply(options.params || {}, this.baseParams);
21510             // if meta was not loaded from remote source.. try requesting it.
21511             if (!this.reader.metaFromRemote) {
21512                 p._requestMeta = 1;
21513             }
21514             if(this.sortInfo && this.remoteSort){
21515                 var pn = this.paramNames;
21516                 p[pn["sort"]] = this.sortInfo.field;
21517                 p[pn["dir"]] = this.sortInfo.direction;
21518             }
21519             if (this.multiSort) {
21520                 var pn = this.paramNames;
21521                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21522             }
21523             
21524             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21525         }
21526     },
21527
21528     /**
21529      * Reloads the Record cache from the configured Proxy using the configured Reader and
21530      * the options from the last load operation performed.
21531      * @param {Object} options (optional) An object containing properties which may override the options
21532      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21533      * the most recently used options are reused).
21534      */
21535     reload : function(options){
21536         this.load(Roo.applyIf(options||{}, this.lastOptions));
21537     },
21538
21539     // private
21540     // Called as a callback by the Reader during a load operation.
21541     loadRecords : function(o, options, success){
21542         if(!o || success === false){
21543             if(success !== false){
21544                 this.fireEvent("load", this, [], options, o);
21545             }
21546             if(options.callback){
21547                 options.callback.call(options.scope || this, [], options, false);
21548             }
21549             return;
21550         }
21551         // if data returned failure - throw an exception.
21552         if (o.success === false) {
21553             // show a message if no listener is registered.
21554             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21555                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21556             }
21557             // loadmask wil be hooked into this..
21558             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21559             return;
21560         }
21561         var r = o.records, t = o.totalRecords || r.length;
21562         
21563         this.fireEvent("beforeloadadd", this, r, options, o);
21564         
21565         if(!options || options.add !== true){
21566             if(this.pruneModifiedRecords){
21567                 this.modified = [];
21568             }
21569             for(var i = 0, len = r.length; i < len; i++){
21570                 r[i].join(this);
21571             }
21572             if(this.snapshot){
21573                 this.data = this.snapshot;
21574                 delete this.snapshot;
21575             }
21576             this.data.clear();
21577             this.data.addAll(r);
21578             this.totalLength = t;
21579             this.applySort();
21580             this.fireEvent("datachanged", this);
21581         }else{
21582             this.totalLength = Math.max(t, this.data.length+r.length);
21583             this.add(r);
21584         }
21585         this.fireEvent("load", this, r, options, o);
21586         if(options.callback){
21587             options.callback.call(options.scope || this, r, options, true);
21588         }
21589     },
21590
21591
21592     /**
21593      * Loads data from a passed data block. A Reader which understands the format of the data
21594      * must have been configured in the constructor.
21595      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21596      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21597      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21598      */
21599     loadData : function(o, append){
21600         var r = this.reader.readRecords(o);
21601         this.loadRecords(r, {add: append}, true);
21602     },
21603
21604     /**
21605      * Gets the number of cached records.
21606      * <p>
21607      * <em>If using paging, this may not be the total size of the dataset. If the data object
21608      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21609      * the data set size</em>
21610      */
21611     getCount : function(){
21612         return this.data.length || 0;
21613     },
21614
21615     /**
21616      * Gets the total number of records in the dataset as returned by the server.
21617      * <p>
21618      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21619      * the dataset size</em>
21620      */
21621     getTotalCount : function(){
21622         return this.totalLength || 0;
21623     },
21624
21625     /**
21626      * Returns the sort state of the Store as an object with two properties:
21627      * <pre><code>
21628  field {String} The name of the field by which the Records are sorted
21629  direction {String} The sort order, "ASC" or "DESC"
21630      * </code></pre>
21631      */
21632     getSortState : function(){
21633         return this.sortInfo;
21634     },
21635
21636     // private
21637     applySort : function(){
21638         if(this.sortInfo && !this.remoteSort){
21639             var s = this.sortInfo, f = s.field;
21640             var st = this.fields.get(f).sortType;
21641             var fn = function(r1, r2){
21642                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21643                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21644             };
21645             this.data.sort(s.direction, fn);
21646             if(this.snapshot && this.snapshot != this.data){
21647                 this.snapshot.sort(s.direction, fn);
21648             }
21649         }
21650     },
21651
21652     /**
21653      * Sets the default sort column and order to be used by the next load operation.
21654      * @param {String} fieldName The name of the field to sort by.
21655      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21656      */
21657     setDefaultSort : function(field, dir){
21658         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21659     },
21660
21661     /**
21662      * Sort the Records.
21663      * If remote sorting is used, the sort is performed on the server, and the cache is
21664      * reloaded. If local sorting is used, the cache is sorted internally.
21665      * @param {String} fieldName The name of the field to sort by.
21666      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21667      */
21668     sort : function(fieldName, dir){
21669         var f = this.fields.get(fieldName);
21670         if(!dir){
21671             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21672             
21673             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21674                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21675             }else{
21676                 dir = f.sortDir;
21677             }
21678         }
21679         this.sortToggle[f.name] = dir;
21680         this.sortInfo = {field: f.name, direction: dir};
21681         if(!this.remoteSort){
21682             this.applySort();
21683             this.fireEvent("datachanged", this);
21684         }else{
21685             this.load(this.lastOptions);
21686         }
21687     },
21688
21689     /**
21690      * Calls the specified function for each of the Records in the cache.
21691      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21692      * Returning <em>false</em> aborts and exits the iteration.
21693      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21694      */
21695     each : function(fn, scope){
21696         this.data.each(fn, scope);
21697     },
21698
21699     /**
21700      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21701      * (e.g., during paging).
21702      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21703      */
21704     getModifiedRecords : function(){
21705         return this.modified;
21706     },
21707
21708     // private
21709     createFilterFn : function(property, value, anyMatch){
21710         if(!value.exec){ // not a regex
21711             value = String(value);
21712             if(value.length == 0){
21713                 return false;
21714             }
21715             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21716         }
21717         return function(r){
21718             return value.test(r.data[property]);
21719         };
21720     },
21721
21722     /**
21723      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21724      * @param {String} property A field on your records
21725      * @param {Number} start The record index to start at (defaults to 0)
21726      * @param {Number} end The last record index to include (defaults to length - 1)
21727      * @return {Number} The sum
21728      */
21729     sum : function(property, start, end){
21730         var rs = this.data.items, v = 0;
21731         start = start || 0;
21732         end = (end || end === 0) ? end : rs.length-1;
21733
21734         for(var i = start; i <= end; i++){
21735             v += (rs[i].data[property] || 0);
21736         }
21737         return v;
21738     },
21739
21740     /**
21741      * Filter the records by a specified property.
21742      * @param {String} field A field on your records
21743      * @param {String/RegExp} value Either a string that the field
21744      * should start with or a RegExp to test against the field
21745      * @param {Boolean} anyMatch True to match any part not just the beginning
21746      */
21747     filter : function(property, value, anyMatch){
21748         var fn = this.createFilterFn(property, value, anyMatch);
21749         return fn ? this.filterBy(fn) : this.clearFilter();
21750     },
21751
21752     /**
21753      * Filter by a function. The specified function will be called with each
21754      * record in this data source. If the function returns true the record is included,
21755      * otherwise it is filtered.
21756      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21757      * @param {Object} scope (optional) The scope of the function (defaults to this)
21758      */
21759     filterBy : function(fn, scope){
21760         this.snapshot = this.snapshot || this.data;
21761         this.data = this.queryBy(fn, scope||this);
21762         this.fireEvent("datachanged", this);
21763     },
21764
21765     /**
21766      * Query the records by a specified property.
21767      * @param {String} field A field on your records
21768      * @param {String/RegExp} value Either a string that the field
21769      * should start with or a RegExp to test against the field
21770      * @param {Boolean} anyMatch True to match any part not just the beginning
21771      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21772      */
21773     query : function(property, value, anyMatch){
21774         var fn = this.createFilterFn(property, value, anyMatch);
21775         return fn ? this.queryBy(fn) : this.data.clone();
21776     },
21777
21778     /**
21779      * Query by a function. The specified function will be called with each
21780      * record in this data source. If the function returns true the record is included
21781      * in the results.
21782      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21783      * @param {Object} scope (optional) The scope of the function (defaults to this)
21784       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21785      **/
21786     queryBy : function(fn, scope){
21787         var data = this.snapshot || this.data;
21788         return data.filterBy(fn, scope||this);
21789     },
21790
21791     /**
21792      * Collects unique values for a particular dataIndex from this store.
21793      * @param {String} dataIndex The property to collect
21794      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21795      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21796      * @return {Array} An array of the unique values
21797      **/
21798     collect : function(dataIndex, allowNull, bypassFilter){
21799         var d = (bypassFilter === true && this.snapshot) ?
21800                 this.snapshot.items : this.data.items;
21801         var v, sv, r = [], l = {};
21802         for(var i = 0, len = d.length; i < len; i++){
21803             v = d[i].data[dataIndex];
21804             sv = String(v);
21805             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21806                 l[sv] = true;
21807                 r[r.length] = v;
21808             }
21809         }
21810         return r;
21811     },
21812
21813     /**
21814      * Revert to a view of the Record cache with no filtering applied.
21815      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21816      */
21817     clearFilter : function(suppressEvent){
21818         if(this.snapshot && this.snapshot != this.data){
21819             this.data = this.snapshot;
21820             delete this.snapshot;
21821             if(suppressEvent !== true){
21822                 this.fireEvent("datachanged", this);
21823             }
21824         }
21825     },
21826
21827     // private
21828     afterEdit : function(record){
21829         if(this.modified.indexOf(record) == -1){
21830             this.modified.push(record);
21831         }
21832         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21833     },
21834     
21835     // private
21836     afterReject : function(record){
21837         this.modified.remove(record);
21838         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21839     },
21840
21841     // private
21842     afterCommit : function(record){
21843         this.modified.remove(record);
21844         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21845     },
21846
21847     /**
21848      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21849      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21850      */
21851     commitChanges : function(){
21852         var m = this.modified.slice(0);
21853         this.modified = [];
21854         for(var i = 0, len = m.length; i < len; i++){
21855             m[i].commit();
21856         }
21857     },
21858
21859     /**
21860      * Cancel outstanding changes on all changed records.
21861      */
21862     rejectChanges : function(){
21863         var m = this.modified.slice(0);
21864         this.modified = [];
21865         for(var i = 0, len = m.length; i < len; i++){
21866             m[i].reject();
21867         }
21868     },
21869
21870     onMetaChange : function(meta, rtype, o){
21871         this.recordType = rtype;
21872         this.fields = rtype.prototype.fields;
21873         delete this.snapshot;
21874         this.sortInfo = meta.sortInfo || this.sortInfo;
21875         this.modified = [];
21876         this.fireEvent('metachange', this, this.reader.meta);
21877     },
21878     
21879     moveIndex : function(data, type)
21880     {
21881         var index = this.indexOf(data);
21882         
21883         var newIndex = index + type;
21884         
21885         this.remove(data);
21886         
21887         this.insert(newIndex, data);
21888         
21889     }
21890 });/*
21891  * Based on:
21892  * Ext JS Library 1.1.1
21893  * Copyright(c) 2006-2007, Ext JS, LLC.
21894  *
21895  * Originally Released Under LGPL - original licence link has changed is not relivant.
21896  *
21897  * Fork - LGPL
21898  * <script type="text/javascript">
21899  */
21900
21901 /**
21902  * @class Roo.data.SimpleStore
21903  * @extends Roo.data.Store
21904  * Small helper class to make creating Stores from Array data easier.
21905  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21906  * @cfg {Array} fields An array of field definition objects, or field name strings.
21907  * @cfg {Array} data The multi-dimensional array of data
21908  * @constructor
21909  * @param {Object} config
21910  */
21911 Roo.data.SimpleStore = function(config){
21912     Roo.data.SimpleStore.superclass.constructor.call(this, {
21913         isLocal : true,
21914         reader: new Roo.data.ArrayReader({
21915                 id: config.id
21916             },
21917             Roo.data.Record.create(config.fields)
21918         ),
21919         proxy : new Roo.data.MemoryProxy(config.data)
21920     });
21921     this.load();
21922 };
21923 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21924  * Based on:
21925  * Ext JS Library 1.1.1
21926  * Copyright(c) 2006-2007, Ext JS, LLC.
21927  *
21928  * Originally Released Under LGPL - original licence link has changed is not relivant.
21929  *
21930  * Fork - LGPL
21931  * <script type="text/javascript">
21932  */
21933
21934 /**
21935 /**
21936  * @extends Roo.data.Store
21937  * @class Roo.data.JsonStore
21938  * Small helper class to make creating Stores for JSON data easier. <br/>
21939 <pre><code>
21940 var store = new Roo.data.JsonStore({
21941     url: 'get-images.php',
21942     root: 'images',
21943     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21944 });
21945 </code></pre>
21946  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21947  * JsonReader and HttpProxy (unless inline data is provided).</b>
21948  * @cfg {Array} fields An array of field definition objects, or field name strings.
21949  * @constructor
21950  * @param {Object} config
21951  */
21952 Roo.data.JsonStore = function(c){
21953     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21954         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21955         reader: new Roo.data.JsonReader(c, c.fields)
21956     }));
21957 };
21958 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21959  * Based on:
21960  * Ext JS Library 1.1.1
21961  * Copyright(c) 2006-2007, Ext JS, LLC.
21962  *
21963  * Originally Released Under LGPL - original licence link has changed is not relivant.
21964  *
21965  * Fork - LGPL
21966  * <script type="text/javascript">
21967  */
21968
21969  
21970 Roo.data.Field = function(config){
21971     if(typeof config == "string"){
21972         config = {name: config};
21973     }
21974     Roo.apply(this, config);
21975     
21976     if(!this.type){
21977         this.type = "auto";
21978     }
21979     
21980     var st = Roo.data.SortTypes;
21981     // named sortTypes are supported, here we look them up
21982     if(typeof this.sortType == "string"){
21983         this.sortType = st[this.sortType];
21984     }
21985     
21986     // set default sortType for strings and dates
21987     if(!this.sortType){
21988         switch(this.type){
21989             case "string":
21990                 this.sortType = st.asUCString;
21991                 break;
21992             case "date":
21993                 this.sortType = st.asDate;
21994                 break;
21995             default:
21996                 this.sortType = st.none;
21997         }
21998     }
21999
22000     // define once
22001     var stripRe = /[\$,%]/g;
22002
22003     // prebuilt conversion function for this field, instead of
22004     // switching every time we're reading a value
22005     if(!this.convert){
22006         var cv, dateFormat = this.dateFormat;
22007         switch(this.type){
22008             case "":
22009             case "auto":
22010             case undefined:
22011                 cv = function(v){ return v; };
22012                 break;
22013             case "string":
22014                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22015                 break;
22016             case "int":
22017                 cv = function(v){
22018                     return v !== undefined && v !== null && v !== '' ?
22019                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22020                     };
22021                 break;
22022             case "float":
22023                 cv = function(v){
22024                     return v !== undefined && v !== null && v !== '' ?
22025                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22026                     };
22027                 break;
22028             case "bool":
22029             case "boolean":
22030                 cv = function(v){ return v === true || v === "true" || v == 1; };
22031                 break;
22032             case "date":
22033                 cv = function(v){
22034                     if(!v){
22035                         return '';
22036                     }
22037                     if(v instanceof Date){
22038                         return v;
22039                     }
22040                     if(dateFormat){
22041                         if(dateFormat == "timestamp"){
22042                             return new Date(v*1000);
22043                         }
22044                         return Date.parseDate(v, dateFormat);
22045                     }
22046                     var parsed = Date.parse(v);
22047                     return parsed ? new Date(parsed) : null;
22048                 };
22049              break;
22050             
22051         }
22052         this.convert = cv;
22053     }
22054 };
22055
22056 Roo.data.Field.prototype = {
22057     dateFormat: null,
22058     defaultValue: "",
22059     mapping: null,
22060     sortType : null,
22061     sortDir : "ASC"
22062 };/*
22063  * Based on:
22064  * Ext JS Library 1.1.1
22065  * Copyright(c) 2006-2007, Ext JS, LLC.
22066  *
22067  * Originally Released Under LGPL - original licence link has changed is not relivant.
22068  *
22069  * Fork - LGPL
22070  * <script type="text/javascript">
22071  */
22072  
22073 // Base class for reading structured data from a data source.  This class is intended to be
22074 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22075
22076 /**
22077  * @class Roo.data.DataReader
22078  * Base class for reading structured data from a data source.  This class is intended to be
22079  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22080  */
22081
22082 Roo.data.DataReader = function(meta, recordType){
22083     
22084     this.meta = meta;
22085     
22086     this.recordType = recordType instanceof Array ? 
22087         Roo.data.Record.create(recordType) : recordType;
22088 };
22089
22090 Roo.data.DataReader.prototype = {
22091      /**
22092      * Create an empty record
22093      * @param {Object} data (optional) - overlay some values
22094      * @return {Roo.data.Record} record created.
22095      */
22096     newRow :  function(d) {
22097         var da =  {};
22098         this.recordType.prototype.fields.each(function(c) {
22099             switch( c.type) {
22100                 case 'int' : da[c.name] = 0; break;
22101                 case 'date' : da[c.name] = new Date(); break;
22102                 case 'float' : da[c.name] = 0.0; break;
22103                 case 'boolean' : da[c.name] = false; break;
22104                 default : da[c.name] = ""; break;
22105             }
22106             
22107         });
22108         return new this.recordType(Roo.apply(da, d));
22109     }
22110     
22111 };/*
22112  * Based on:
22113  * Ext JS Library 1.1.1
22114  * Copyright(c) 2006-2007, Ext JS, LLC.
22115  *
22116  * Originally Released Under LGPL - original licence link has changed is not relivant.
22117  *
22118  * Fork - LGPL
22119  * <script type="text/javascript">
22120  */
22121
22122 /**
22123  * @class Roo.data.DataProxy
22124  * @extends Roo.data.Observable
22125  * This class is an abstract base class for implementations which provide retrieval of
22126  * unformatted data objects.<br>
22127  * <p>
22128  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22129  * (of the appropriate type which knows how to parse the data object) to provide a block of
22130  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22131  * <p>
22132  * Custom implementations must implement the load method as described in
22133  * {@link Roo.data.HttpProxy#load}.
22134  */
22135 Roo.data.DataProxy = function(){
22136     this.addEvents({
22137         /**
22138          * @event beforeload
22139          * Fires before a network request is made to retrieve a data object.
22140          * @param {Object} This DataProxy object.
22141          * @param {Object} params The params parameter to the load function.
22142          */
22143         beforeload : true,
22144         /**
22145          * @event load
22146          * Fires before the load method's callback is called.
22147          * @param {Object} This DataProxy object.
22148          * @param {Object} o The data object.
22149          * @param {Object} arg The callback argument object passed to the load function.
22150          */
22151         load : true,
22152         /**
22153          * @event loadexception
22154          * Fires if an Exception occurs during data retrieval.
22155          * @param {Object} This DataProxy object.
22156          * @param {Object} o The data object.
22157          * @param {Object} arg The callback argument object passed to the load function.
22158          * @param {Object} e The Exception.
22159          */
22160         loadexception : true
22161     });
22162     Roo.data.DataProxy.superclass.constructor.call(this);
22163 };
22164
22165 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22166
22167     /**
22168      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22169      */
22170 /*
22171  * Based on:
22172  * Ext JS Library 1.1.1
22173  * Copyright(c) 2006-2007, Ext JS, LLC.
22174  *
22175  * Originally Released Under LGPL - original licence link has changed is not relivant.
22176  *
22177  * Fork - LGPL
22178  * <script type="text/javascript">
22179  */
22180 /**
22181  * @class Roo.data.MemoryProxy
22182  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22183  * to the Reader when its load method is called.
22184  * @constructor
22185  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22186  */
22187 Roo.data.MemoryProxy = function(data){
22188     if (data.data) {
22189         data = data.data;
22190     }
22191     Roo.data.MemoryProxy.superclass.constructor.call(this);
22192     this.data = data;
22193 };
22194
22195 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22196     /**
22197      * Load data from the requested source (in this case an in-memory
22198      * data object passed to the constructor), read the data object into
22199      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22200      * process that block using the passed callback.
22201      * @param {Object} params This parameter is not used by the MemoryProxy class.
22202      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22203      * object into a block of Roo.data.Records.
22204      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22205      * The function must be passed <ul>
22206      * <li>The Record block object</li>
22207      * <li>The "arg" argument from the load function</li>
22208      * <li>A boolean success indicator</li>
22209      * </ul>
22210      * @param {Object} scope The scope in which to call the callback
22211      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22212      */
22213     load : function(params, reader, callback, scope, arg){
22214         params = params || {};
22215         var result;
22216         try {
22217             result = reader.readRecords(this.data);
22218         }catch(e){
22219             this.fireEvent("loadexception", this, arg, null, e);
22220             callback.call(scope, null, arg, false);
22221             return;
22222         }
22223         callback.call(scope, result, arg, true);
22224     },
22225     
22226     // private
22227     update : function(params, records){
22228         
22229     }
22230 });/*
22231  * Based on:
22232  * Ext JS Library 1.1.1
22233  * Copyright(c) 2006-2007, Ext JS, LLC.
22234  *
22235  * Originally Released Under LGPL - original licence link has changed is not relivant.
22236  *
22237  * Fork - LGPL
22238  * <script type="text/javascript">
22239  */
22240 /**
22241  * @class Roo.data.HttpProxy
22242  * @extends Roo.data.DataProxy
22243  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22244  * configured to reference a certain URL.<br><br>
22245  * <p>
22246  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22247  * from which the running page was served.<br><br>
22248  * <p>
22249  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22250  * <p>
22251  * Be aware that to enable the browser to parse an XML document, the server must set
22252  * the Content-Type header in the HTTP response to "text/xml".
22253  * @constructor
22254  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22255  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22256  * will be used to make the request.
22257  */
22258 Roo.data.HttpProxy = function(conn){
22259     Roo.data.HttpProxy.superclass.constructor.call(this);
22260     // is conn a conn config or a real conn?
22261     this.conn = conn;
22262     this.useAjax = !conn || !conn.events;
22263   
22264 };
22265
22266 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22267     // thse are take from connection...
22268     
22269     /**
22270      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22271      */
22272     /**
22273      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22274      * extra parameters to each request made by this object. (defaults to undefined)
22275      */
22276     /**
22277      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22278      *  to each request made by this object. (defaults to undefined)
22279      */
22280     /**
22281      * @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)
22282      */
22283     /**
22284      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22285      */
22286      /**
22287      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22288      * @type Boolean
22289      */
22290   
22291
22292     /**
22293      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22294      * @type Boolean
22295      */
22296     /**
22297      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22298      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22299      * a finer-grained basis than the DataProxy events.
22300      */
22301     getConnection : function(){
22302         return this.useAjax ? Roo.Ajax : this.conn;
22303     },
22304
22305     /**
22306      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22307      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22308      * process that block using the passed callback.
22309      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22310      * for the request to the remote server.
22311      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22312      * object into a block of Roo.data.Records.
22313      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22314      * The function must be passed <ul>
22315      * <li>The Record block object</li>
22316      * <li>The "arg" argument from the load function</li>
22317      * <li>A boolean success indicator</li>
22318      * </ul>
22319      * @param {Object} scope The scope in which to call the callback
22320      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22321      */
22322     load : function(params, reader, callback, scope, arg){
22323         if(this.fireEvent("beforeload", this, params) !== false){
22324             var  o = {
22325                 params : params || {},
22326                 request: {
22327                     callback : callback,
22328                     scope : scope,
22329                     arg : arg
22330                 },
22331                 reader: reader,
22332                 callback : this.loadResponse,
22333                 scope: this
22334             };
22335             if(this.useAjax){
22336                 Roo.applyIf(o, this.conn);
22337                 if(this.activeRequest){
22338                     Roo.Ajax.abort(this.activeRequest);
22339                 }
22340                 this.activeRequest = Roo.Ajax.request(o);
22341             }else{
22342                 this.conn.request(o);
22343             }
22344         }else{
22345             callback.call(scope||this, null, arg, false);
22346         }
22347     },
22348
22349     // private
22350     loadResponse : function(o, success, response){
22351         delete this.activeRequest;
22352         if(!success){
22353             this.fireEvent("loadexception", this, o, response);
22354             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22355             return;
22356         }
22357         var result;
22358         try {
22359             result = o.reader.read(response);
22360         }catch(e){
22361             this.fireEvent("loadexception", this, o, response, e);
22362             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22363             return;
22364         }
22365         
22366         this.fireEvent("load", this, o, o.request.arg);
22367         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22368     },
22369
22370     // private
22371     update : function(dataSet){
22372
22373     },
22374
22375     // private
22376     updateResponse : function(dataSet){
22377
22378     }
22379 });/*
22380  * Based on:
22381  * Ext JS Library 1.1.1
22382  * Copyright(c) 2006-2007, Ext JS, LLC.
22383  *
22384  * Originally Released Under LGPL - original licence link has changed is not relivant.
22385  *
22386  * Fork - LGPL
22387  * <script type="text/javascript">
22388  */
22389
22390 /**
22391  * @class Roo.data.ScriptTagProxy
22392  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22393  * other than the originating domain of the running page.<br><br>
22394  * <p>
22395  * <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
22396  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22397  * <p>
22398  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22399  * source code that is used as the source inside a &lt;script> tag.<br><br>
22400  * <p>
22401  * In order for the browser to process the returned data, the server must wrap the data object
22402  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22403  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22404  * depending on whether the callback name was passed:
22405  * <p>
22406  * <pre><code>
22407 boolean scriptTag = false;
22408 String cb = request.getParameter("callback");
22409 if (cb != null) {
22410     scriptTag = true;
22411     response.setContentType("text/javascript");
22412 } else {
22413     response.setContentType("application/x-json");
22414 }
22415 Writer out = response.getWriter();
22416 if (scriptTag) {
22417     out.write(cb + "(");
22418 }
22419 out.print(dataBlock.toJsonString());
22420 if (scriptTag) {
22421     out.write(");");
22422 }
22423 </pre></code>
22424  *
22425  * @constructor
22426  * @param {Object} config A configuration object.
22427  */
22428 Roo.data.ScriptTagProxy = function(config){
22429     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22430     Roo.apply(this, config);
22431     this.head = document.getElementsByTagName("head")[0];
22432 };
22433
22434 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22435
22436 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22437     /**
22438      * @cfg {String} url The URL from which to request the data object.
22439      */
22440     /**
22441      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22442      */
22443     timeout : 30000,
22444     /**
22445      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22446      * the server the name of the callback function set up by the load call to process the returned data object.
22447      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22448      * javascript output which calls this named function passing the data object as its only parameter.
22449      */
22450     callbackParam : "callback",
22451     /**
22452      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22453      * name to the request.
22454      */
22455     nocache : true,
22456
22457     /**
22458      * Load data from the configured URL, read the data object into
22459      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22460      * process that block using the passed callback.
22461      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22462      * for the request to the remote server.
22463      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22464      * object into a block of Roo.data.Records.
22465      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22466      * The function must be passed <ul>
22467      * <li>The Record block object</li>
22468      * <li>The "arg" argument from the load function</li>
22469      * <li>A boolean success indicator</li>
22470      * </ul>
22471      * @param {Object} scope The scope in which to call the callback
22472      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22473      */
22474     load : function(params, reader, callback, scope, arg){
22475         if(this.fireEvent("beforeload", this, params) !== false){
22476
22477             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22478
22479             var url = this.url;
22480             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22481             if(this.nocache){
22482                 url += "&_dc=" + (new Date().getTime());
22483             }
22484             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22485             var trans = {
22486                 id : transId,
22487                 cb : "stcCallback"+transId,
22488                 scriptId : "stcScript"+transId,
22489                 params : params,
22490                 arg : arg,
22491                 url : url,
22492                 callback : callback,
22493                 scope : scope,
22494                 reader : reader
22495             };
22496             var conn = this;
22497
22498             window[trans.cb] = function(o){
22499                 conn.handleResponse(o, trans);
22500             };
22501
22502             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22503
22504             if(this.autoAbort !== false){
22505                 this.abort();
22506             }
22507
22508             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22509
22510             var script = document.createElement("script");
22511             script.setAttribute("src", url);
22512             script.setAttribute("type", "text/javascript");
22513             script.setAttribute("id", trans.scriptId);
22514             this.head.appendChild(script);
22515
22516             this.trans = trans;
22517         }else{
22518             callback.call(scope||this, null, arg, false);
22519         }
22520     },
22521
22522     // private
22523     isLoading : function(){
22524         return this.trans ? true : false;
22525     },
22526
22527     /**
22528      * Abort the current server request.
22529      */
22530     abort : function(){
22531         if(this.isLoading()){
22532             this.destroyTrans(this.trans);
22533         }
22534     },
22535
22536     // private
22537     destroyTrans : function(trans, isLoaded){
22538         this.head.removeChild(document.getElementById(trans.scriptId));
22539         clearTimeout(trans.timeoutId);
22540         if(isLoaded){
22541             window[trans.cb] = undefined;
22542             try{
22543                 delete window[trans.cb];
22544             }catch(e){}
22545         }else{
22546             // if hasn't been loaded, wait for load to remove it to prevent script error
22547             window[trans.cb] = function(){
22548                 window[trans.cb] = undefined;
22549                 try{
22550                     delete window[trans.cb];
22551                 }catch(e){}
22552             };
22553         }
22554     },
22555
22556     // private
22557     handleResponse : function(o, trans){
22558         this.trans = false;
22559         this.destroyTrans(trans, true);
22560         var result;
22561         try {
22562             result = trans.reader.readRecords(o);
22563         }catch(e){
22564             this.fireEvent("loadexception", this, o, trans.arg, e);
22565             trans.callback.call(trans.scope||window, null, trans.arg, false);
22566             return;
22567         }
22568         this.fireEvent("load", this, o, trans.arg);
22569         trans.callback.call(trans.scope||window, result, trans.arg, true);
22570     },
22571
22572     // private
22573     handleFailure : function(trans){
22574         this.trans = false;
22575         this.destroyTrans(trans, false);
22576         this.fireEvent("loadexception", this, null, trans.arg);
22577         trans.callback.call(trans.scope||window, null, trans.arg, false);
22578     }
22579 });/*
22580  * Based on:
22581  * Ext JS Library 1.1.1
22582  * Copyright(c) 2006-2007, Ext JS, LLC.
22583  *
22584  * Originally Released Under LGPL - original licence link has changed is not relivant.
22585  *
22586  * Fork - LGPL
22587  * <script type="text/javascript">
22588  */
22589
22590 /**
22591  * @class Roo.data.JsonReader
22592  * @extends Roo.data.DataReader
22593  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22594  * based on mappings in a provided Roo.data.Record constructor.
22595  * 
22596  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22597  * in the reply previously. 
22598  * 
22599  * <p>
22600  * Example code:
22601  * <pre><code>
22602 var RecordDef = Roo.data.Record.create([
22603     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22604     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22605 ]);
22606 var myReader = new Roo.data.JsonReader({
22607     totalProperty: "results",    // The property which contains the total dataset size (optional)
22608     root: "rows",                // The property which contains an Array of row objects
22609     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22610 }, RecordDef);
22611 </code></pre>
22612  * <p>
22613  * This would consume a JSON file like this:
22614  * <pre><code>
22615 { 'results': 2, 'rows': [
22616     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22617     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22618 }
22619 </code></pre>
22620  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22621  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22622  * paged from the remote server.
22623  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22624  * @cfg {String} root name of the property which contains the Array of row objects.
22625  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22626  * @cfg {Array} fields Array of field definition objects
22627  * @constructor
22628  * Create a new JsonReader
22629  * @param {Object} meta Metadata configuration options
22630  * @param {Object} recordType Either an Array of field definition objects,
22631  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22632  */
22633 Roo.data.JsonReader = function(meta, recordType){
22634     
22635     meta = meta || {};
22636     // set some defaults:
22637     Roo.applyIf(meta, {
22638         totalProperty: 'total',
22639         successProperty : 'success',
22640         root : 'data',
22641         id : 'id'
22642     });
22643     
22644     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22645 };
22646 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22647     
22648     /**
22649      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22650      * Used by Store query builder to append _requestMeta to params.
22651      * 
22652      */
22653     metaFromRemote : false,
22654     /**
22655      * This method is only used by a DataProxy which has retrieved data from a remote server.
22656      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22657      * @return {Object} data A data block which is used by an Roo.data.Store object as
22658      * a cache of Roo.data.Records.
22659      */
22660     read : function(response){
22661         var json = response.responseText;
22662        
22663         var o = /* eval:var:o */ eval("("+json+")");
22664         if(!o) {
22665             throw {message: "JsonReader.read: Json object not found"};
22666         }
22667         
22668         if(o.metaData){
22669             
22670             delete this.ef;
22671             this.metaFromRemote = true;
22672             this.meta = o.metaData;
22673             this.recordType = Roo.data.Record.create(o.metaData.fields);
22674             this.onMetaChange(this.meta, this.recordType, o);
22675         }
22676         return this.readRecords(o);
22677     },
22678
22679     // private function a store will implement
22680     onMetaChange : function(meta, recordType, o){
22681
22682     },
22683
22684     /**
22685          * @ignore
22686          */
22687     simpleAccess: function(obj, subsc) {
22688         return obj[subsc];
22689     },
22690
22691         /**
22692          * @ignore
22693          */
22694     getJsonAccessor: function(){
22695         var re = /[\[\.]/;
22696         return function(expr) {
22697             try {
22698                 return(re.test(expr))
22699                     ? new Function("obj", "return obj." + expr)
22700                     : function(obj){
22701                         return obj[expr];
22702                     };
22703             } catch(e){}
22704             return Roo.emptyFn;
22705         };
22706     }(),
22707
22708     /**
22709      * Create a data block containing Roo.data.Records from an XML document.
22710      * @param {Object} o An object which contains an Array of row objects in the property specified
22711      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22712      * which contains the total size of the dataset.
22713      * @return {Object} data A data block which is used by an Roo.data.Store object as
22714      * a cache of Roo.data.Records.
22715      */
22716     readRecords : function(o){
22717         /**
22718          * After any data loads, the raw JSON data is available for further custom processing.
22719          * @type Object
22720          */
22721         this.o = o;
22722         var s = this.meta, Record = this.recordType,
22723             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22724
22725 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22726         if (!this.ef) {
22727             if(s.totalProperty) {
22728                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22729                 }
22730                 if(s.successProperty) {
22731                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22732                 }
22733                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22734                 if (s.id) {
22735                         var g = this.getJsonAccessor(s.id);
22736                         this.getId = function(rec) {
22737                                 var r = g(rec);  
22738                                 return (r === undefined || r === "") ? null : r;
22739                         };
22740                 } else {
22741                         this.getId = function(){return null;};
22742                 }
22743             this.ef = [];
22744             for(var jj = 0; jj < fl; jj++){
22745                 f = fi[jj];
22746                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22747                 this.ef[jj] = this.getJsonAccessor(map);
22748             }
22749         }
22750
22751         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22752         if(s.totalProperty){
22753             var vt = parseInt(this.getTotal(o), 10);
22754             if(!isNaN(vt)){
22755                 totalRecords = vt;
22756             }
22757         }
22758         if(s.successProperty){
22759             var vs = this.getSuccess(o);
22760             if(vs === false || vs === 'false'){
22761                 success = false;
22762             }
22763         }
22764         var records = [];
22765         for(var i = 0; i < c; i++){
22766                 var n = root[i];
22767             var values = {};
22768             var id = this.getId(n);
22769             for(var j = 0; j < fl; j++){
22770                 f = fi[j];
22771             var v = this.ef[j](n);
22772             if (!f.convert) {
22773                 Roo.log('missing convert for ' + f.name);
22774                 Roo.log(f);
22775                 continue;
22776             }
22777             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22778             }
22779             var record = new Record(values, id);
22780             record.json = n;
22781             records[i] = record;
22782         }
22783         return {
22784             raw : o,
22785             success : success,
22786             records : records,
22787             totalRecords : totalRecords
22788         };
22789     }
22790 });/*
22791  * Based on:
22792  * Ext JS Library 1.1.1
22793  * Copyright(c) 2006-2007, Ext JS, LLC.
22794  *
22795  * Originally Released Under LGPL - original licence link has changed is not relivant.
22796  *
22797  * Fork - LGPL
22798  * <script type="text/javascript">
22799  */
22800
22801 /**
22802  * @class Roo.data.XmlReader
22803  * @extends Roo.data.DataReader
22804  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22805  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22806  * <p>
22807  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22808  * header in the HTTP response must be set to "text/xml".</em>
22809  * <p>
22810  * Example code:
22811  * <pre><code>
22812 var RecordDef = Roo.data.Record.create([
22813    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22814    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22815 ]);
22816 var myReader = new Roo.data.XmlReader({
22817    totalRecords: "results", // The element which contains the total dataset size (optional)
22818    record: "row",           // The repeated element which contains row information
22819    id: "id"                 // The element within the row that provides an ID for the record (optional)
22820 }, RecordDef);
22821 </code></pre>
22822  * <p>
22823  * This would consume an XML file like this:
22824  * <pre><code>
22825 &lt;?xml?>
22826 &lt;dataset>
22827  &lt;results>2&lt;/results>
22828  &lt;row>
22829    &lt;id>1&lt;/id>
22830    &lt;name>Bill&lt;/name>
22831    &lt;occupation>Gardener&lt;/occupation>
22832  &lt;/row>
22833  &lt;row>
22834    &lt;id>2&lt;/id>
22835    &lt;name>Ben&lt;/name>
22836    &lt;occupation>Horticulturalist&lt;/occupation>
22837  &lt;/row>
22838 &lt;/dataset>
22839 </code></pre>
22840  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22841  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22842  * paged from the remote server.
22843  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22844  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22845  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22846  * a record identifier value.
22847  * @constructor
22848  * Create a new XmlReader
22849  * @param {Object} meta Metadata configuration options
22850  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22851  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22852  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22853  */
22854 Roo.data.XmlReader = function(meta, recordType){
22855     meta = meta || {};
22856     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22857 };
22858 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22859     /**
22860      * This method is only used by a DataProxy which has retrieved data from a remote server.
22861          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22862          * to contain a method called 'responseXML' that returns an XML document object.
22863      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22864      * a cache of Roo.data.Records.
22865      */
22866     read : function(response){
22867         var doc = response.responseXML;
22868         if(!doc) {
22869             throw {message: "XmlReader.read: XML Document not available"};
22870         }
22871         return this.readRecords(doc);
22872     },
22873
22874     /**
22875      * Create a data block containing Roo.data.Records from an XML document.
22876          * @param {Object} doc A parsed XML document.
22877      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22878      * a cache of Roo.data.Records.
22879      */
22880     readRecords : function(doc){
22881         /**
22882          * After any data loads/reads, the raw XML Document is available for further custom processing.
22883          * @type XMLDocument
22884          */
22885         this.xmlData = doc;
22886         var root = doc.documentElement || doc;
22887         var q = Roo.DomQuery;
22888         var recordType = this.recordType, fields = recordType.prototype.fields;
22889         var sid = this.meta.id;
22890         var totalRecords = 0, success = true;
22891         if(this.meta.totalRecords){
22892             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22893         }
22894         
22895         if(this.meta.success){
22896             var sv = q.selectValue(this.meta.success, root, true);
22897             success = sv !== false && sv !== 'false';
22898         }
22899         var records = [];
22900         var ns = q.select(this.meta.record, root);
22901         for(var i = 0, len = ns.length; i < len; i++) {
22902                 var n = ns[i];
22903                 var values = {};
22904                 var id = sid ? q.selectValue(sid, n) : undefined;
22905                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22906                     var f = fields.items[j];
22907                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22908                     v = f.convert(v);
22909                     values[f.name] = v;
22910                 }
22911                 var record = new recordType(values, id);
22912                 record.node = n;
22913                 records[records.length] = record;
22914             }
22915
22916             return {
22917                 success : success,
22918                 records : records,
22919                 totalRecords : totalRecords || records.length
22920             };
22921     }
22922 });/*
22923  * Based on:
22924  * Ext JS Library 1.1.1
22925  * Copyright(c) 2006-2007, Ext JS, LLC.
22926  *
22927  * Originally Released Under LGPL - original licence link has changed is not relivant.
22928  *
22929  * Fork - LGPL
22930  * <script type="text/javascript">
22931  */
22932
22933 /**
22934  * @class Roo.data.ArrayReader
22935  * @extends Roo.data.DataReader
22936  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22937  * Each element of that Array represents a row of data fields. The
22938  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22939  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22940  * <p>
22941  * Example code:.
22942  * <pre><code>
22943 var RecordDef = Roo.data.Record.create([
22944     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22945     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22946 ]);
22947 var myReader = new Roo.data.ArrayReader({
22948     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22949 }, RecordDef);
22950 </code></pre>
22951  * <p>
22952  * This would consume an Array like this:
22953  * <pre><code>
22954 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22955   </code></pre>
22956  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22957  * @constructor
22958  * Create a new JsonReader
22959  * @param {Object} meta Metadata configuration options.
22960  * @param {Object} recordType Either an Array of field definition objects
22961  * as specified to {@link Roo.data.Record#create},
22962  * or an {@link Roo.data.Record} object
22963  * created using {@link Roo.data.Record#create}.
22964  */
22965 Roo.data.ArrayReader = function(meta, recordType){
22966     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22967 };
22968
22969 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22970     /**
22971      * Create a data block containing Roo.data.Records from an XML document.
22972      * @param {Object} o An Array of row objects which represents the dataset.
22973      * @return {Object} data A data block which is used by an Roo.data.Store object as
22974      * a cache of Roo.data.Records.
22975      */
22976     readRecords : function(o){
22977         var sid = this.meta ? this.meta.id : null;
22978         var recordType = this.recordType, fields = recordType.prototype.fields;
22979         var records = [];
22980         var root = o;
22981             for(var i = 0; i < root.length; i++){
22982                     var n = root[i];
22983                 var values = {};
22984                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22985                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22986                 var f = fields.items[j];
22987                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22988                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22989                 v = f.convert(v);
22990                 values[f.name] = v;
22991             }
22992                 var record = new recordType(values, id);
22993                 record.json = n;
22994                 records[records.length] = record;
22995             }
22996             return {
22997                 records : records,
22998                 totalRecords : records.length
22999             };
23000     }
23001 });/*
23002  * Based on:
23003  * Ext JS Library 1.1.1
23004  * Copyright(c) 2006-2007, Ext JS, LLC.
23005  *
23006  * Originally Released Under LGPL - original licence link has changed is not relivant.
23007  *
23008  * Fork - LGPL
23009  * <script type="text/javascript">
23010  */
23011
23012
23013 /**
23014  * @class Roo.data.Tree
23015  * @extends Roo.util.Observable
23016  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23017  * in the tree have most standard DOM functionality.
23018  * @constructor
23019  * @param {Node} root (optional) The root node
23020  */
23021 Roo.data.Tree = function(root){
23022    this.nodeHash = {};
23023    /**
23024     * The root node for this tree
23025     * @type Node
23026     */
23027    this.root = null;
23028    if(root){
23029        this.setRootNode(root);
23030    }
23031    this.addEvents({
23032        /**
23033         * @event append
23034         * Fires when a new child node is appended to a node in this tree.
23035         * @param {Tree} tree The owner tree
23036         * @param {Node} parent The parent node
23037         * @param {Node} node The newly appended node
23038         * @param {Number} index The index of the newly appended node
23039         */
23040        "append" : true,
23041        /**
23042         * @event remove
23043         * Fires when a child node is removed from a node in this tree.
23044         * @param {Tree} tree The owner tree
23045         * @param {Node} parent The parent node
23046         * @param {Node} node The child node removed
23047         */
23048        "remove" : true,
23049        /**
23050         * @event move
23051         * Fires when a node is moved to a new location in the tree
23052         * @param {Tree} tree The owner tree
23053         * @param {Node} node The node moved
23054         * @param {Node} oldParent The old parent of this node
23055         * @param {Node} newParent The new parent of this node
23056         * @param {Number} index The index it was moved to
23057         */
23058        "move" : true,
23059        /**
23060         * @event insert
23061         * Fires when a new child node is inserted in a node in this tree.
23062         * @param {Tree} tree The owner tree
23063         * @param {Node} parent The parent node
23064         * @param {Node} node The child node inserted
23065         * @param {Node} refNode The child node the node was inserted before
23066         */
23067        "insert" : true,
23068        /**
23069         * @event beforeappend
23070         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23071         * @param {Tree} tree The owner tree
23072         * @param {Node} parent The parent node
23073         * @param {Node} node The child node to be appended
23074         */
23075        "beforeappend" : true,
23076        /**
23077         * @event beforeremove
23078         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23079         * @param {Tree} tree The owner tree
23080         * @param {Node} parent The parent node
23081         * @param {Node} node The child node to be removed
23082         */
23083        "beforeremove" : true,
23084        /**
23085         * @event beforemove
23086         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23087         * @param {Tree} tree The owner tree
23088         * @param {Node} node The node being moved
23089         * @param {Node} oldParent The parent of the node
23090         * @param {Node} newParent The new parent the node is moving to
23091         * @param {Number} index The index it is being moved to
23092         */
23093        "beforemove" : true,
23094        /**
23095         * @event beforeinsert
23096         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23097         * @param {Tree} tree The owner tree
23098         * @param {Node} parent The parent node
23099         * @param {Node} node The child node to be inserted
23100         * @param {Node} refNode The child node the node is being inserted before
23101         */
23102        "beforeinsert" : true
23103    });
23104
23105     Roo.data.Tree.superclass.constructor.call(this);
23106 };
23107
23108 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23109     pathSeparator: "/",
23110
23111     proxyNodeEvent : function(){
23112         return this.fireEvent.apply(this, arguments);
23113     },
23114
23115     /**
23116      * Returns the root node for this tree.
23117      * @return {Node}
23118      */
23119     getRootNode : function(){
23120         return this.root;
23121     },
23122
23123     /**
23124      * Sets the root node for this tree.
23125      * @param {Node} node
23126      * @return {Node}
23127      */
23128     setRootNode : function(node){
23129         this.root = node;
23130         node.ownerTree = this;
23131         node.isRoot = true;
23132         this.registerNode(node);
23133         return node;
23134     },
23135
23136     /**
23137      * Gets a node in this tree by its id.
23138      * @param {String} id
23139      * @return {Node}
23140      */
23141     getNodeById : function(id){
23142         return this.nodeHash[id];
23143     },
23144
23145     registerNode : function(node){
23146         this.nodeHash[node.id] = node;
23147     },
23148
23149     unregisterNode : function(node){
23150         delete this.nodeHash[node.id];
23151     },
23152
23153     toString : function(){
23154         return "[Tree"+(this.id?" "+this.id:"")+"]";
23155     }
23156 });
23157
23158 /**
23159  * @class Roo.data.Node
23160  * @extends Roo.util.Observable
23161  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23162  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23163  * @constructor
23164  * @param {Object} attributes The attributes/config for the node
23165  */
23166 Roo.data.Node = function(attributes){
23167     /**
23168      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23169      * @type {Object}
23170      */
23171     this.attributes = attributes || {};
23172     this.leaf = this.attributes.leaf;
23173     /**
23174      * The node id. @type String
23175      */
23176     this.id = this.attributes.id;
23177     if(!this.id){
23178         this.id = Roo.id(null, "ynode-");
23179         this.attributes.id = this.id;
23180     }
23181      
23182     
23183     /**
23184      * All child nodes of this node. @type Array
23185      */
23186     this.childNodes = [];
23187     if(!this.childNodes.indexOf){ // indexOf is a must
23188         this.childNodes.indexOf = function(o){
23189             for(var i = 0, len = this.length; i < len; i++){
23190                 if(this[i] == o) {
23191                     return i;
23192                 }
23193             }
23194             return -1;
23195         };
23196     }
23197     /**
23198      * The parent node for this node. @type Node
23199      */
23200     this.parentNode = null;
23201     /**
23202      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23203      */
23204     this.firstChild = null;
23205     /**
23206      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23207      */
23208     this.lastChild = null;
23209     /**
23210      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23211      */
23212     this.previousSibling = null;
23213     /**
23214      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23215      */
23216     this.nextSibling = null;
23217
23218     this.addEvents({
23219        /**
23220         * @event append
23221         * Fires when a new child node is appended
23222         * @param {Tree} tree The owner tree
23223         * @param {Node} this This node
23224         * @param {Node} node The newly appended node
23225         * @param {Number} index The index of the newly appended node
23226         */
23227        "append" : true,
23228        /**
23229         * @event remove
23230         * Fires when a child node is removed
23231         * @param {Tree} tree The owner tree
23232         * @param {Node} this This node
23233         * @param {Node} node The removed node
23234         */
23235        "remove" : true,
23236        /**
23237         * @event move
23238         * Fires when this node is moved to a new location in the tree
23239         * @param {Tree} tree The owner tree
23240         * @param {Node} this This node
23241         * @param {Node} oldParent The old parent of this node
23242         * @param {Node} newParent The new parent of this node
23243         * @param {Number} index The index it was moved to
23244         */
23245        "move" : true,
23246        /**
23247         * @event insert
23248         * Fires when a new child node is inserted.
23249         * @param {Tree} tree The owner tree
23250         * @param {Node} this This node
23251         * @param {Node} node The child node inserted
23252         * @param {Node} refNode The child node the node was inserted before
23253         */
23254        "insert" : true,
23255        /**
23256         * @event beforeappend
23257         * Fires before a new child is appended, return false to cancel the append.
23258         * @param {Tree} tree The owner tree
23259         * @param {Node} this This node
23260         * @param {Node} node The child node to be appended
23261         */
23262        "beforeappend" : true,
23263        /**
23264         * @event beforeremove
23265         * Fires before a child is removed, return false to cancel the remove.
23266         * @param {Tree} tree The owner tree
23267         * @param {Node} this This node
23268         * @param {Node} node The child node to be removed
23269         */
23270        "beforeremove" : true,
23271        /**
23272         * @event beforemove
23273         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23274         * @param {Tree} tree The owner tree
23275         * @param {Node} this This node
23276         * @param {Node} oldParent The parent of this node
23277         * @param {Node} newParent The new parent this node is moving to
23278         * @param {Number} index The index it is being moved to
23279         */
23280        "beforemove" : true,
23281        /**
23282         * @event beforeinsert
23283         * Fires before a new child is inserted, return false to cancel the insert.
23284         * @param {Tree} tree The owner tree
23285         * @param {Node} this This node
23286         * @param {Node} node The child node to be inserted
23287         * @param {Node} refNode The child node the node is being inserted before
23288         */
23289        "beforeinsert" : true
23290    });
23291     this.listeners = this.attributes.listeners;
23292     Roo.data.Node.superclass.constructor.call(this);
23293 };
23294
23295 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23296     fireEvent : function(evtName){
23297         // first do standard event for this node
23298         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23299             return false;
23300         }
23301         // then bubble it up to the tree if the event wasn't cancelled
23302         var ot = this.getOwnerTree();
23303         if(ot){
23304             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23305                 return false;
23306             }
23307         }
23308         return true;
23309     },
23310
23311     /**
23312      * Returns true if this node is a leaf
23313      * @return {Boolean}
23314      */
23315     isLeaf : function(){
23316         return this.leaf === true;
23317     },
23318
23319     // private
23320     setFirstChild : function(node){
23321         this.firstChild = node;
23322     },
23323
23324     //private
23325     setLastChild : function(node){
23326         this.lastChild = node;
23327     },
23328
23329
23330     /**
23331      * Returns true if this node is the last child of its parent
23332      * @return {Boolean}
23333      */
23334     isLast : function(){
23335        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23336     },
23337
23338     /**
23339      * Returns true if this node is the first child of its parent
23340      * @return {Boolean}
23341      */
23342     isFirst : function(){
23343        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23344     },
23345
23346     hasChildNodes : function(){
23347         return !this.isLeaf() && this.childNodes.length > 0;
23348     },
23349
23350     /**
23351      * Insert node(s) as the last child node of this node.
23352      * @param {Node/Array} node The node or Array of nodes to append
23353      * @return {Node} The appended node if single append, or null if an array was passed
23354      */
23355     appendChild : function(node){
23356         var multi = false;
23357         if(node instanceof Array){
23358             multi = node;
23359         }else if(arguments.length > 1){
23360             multi = arguments;
23361         }
23362         // if passed an array or multiple args do them one by one
23363         if(multi){
23364             for(var i = 0, len = multi.length; i < len; i++) {
23365                 this.appendChild(multi[i]);
23366             }
23367         }else{
23368             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23369                 return false;
23370             }
23371             var index = this.childNodes.length;
23372             var oldParent = node.parentNode;
23373             // it's a move, make sure we move it cleanly
23374             if(oldParent){
23375                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23376                     return false;
23377                 }
23378                 oldParent.removeChild(node);
23379             }
23380             index = this.childNodes.length;
23381             if(index == 0){
23382                 this.setFirstChild(node);
23383             }
23384             this.childNodes.push(node);
23385             node.parentNode = this;
23386             var ps = this.childNodes[index-1];
23387             if(ps){
23388                 node.previousSibling = ps;
23389                 ps.nextSibling = node;
23390             }else{
23391                 node.previousSibling = null;
23392             }
23393             node.nextSibling = null;
23394             this.setLastChild(node);
23395             node.setOwnerTree(this.getOwnerTree());
23396             this.fireEvent("append", this.ownerTree, this, node, index);
23397             if(oldParent){
23398                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23399             }
23400             return node;
23401         }
23402     },
23403
23404     /**
23405      * Removes a child node from this node.
23406      * @param {Node} node The node to remove
23407      * @return {Node} The removed node
23408      */
23409     removeChild : function(node){
23410         var index = this.childNodes.indexOf(node);
23411         if(index == -1){
23412             return false;
23413         }
23414         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23415             return false;
23416         }
23417
23418         // remove it from childNodes collection
23419         this.childNodes.splice(index, 1);
23420
23421         // update siblings
23422         if(node.previousSibling){
23423             node.previousSibling.nextSibling = node.nextSibling;
23424         }
23425         if(node.nextSibling){
23426             node.nextSibling.previousSibling = node.previousSibling;
23427         }
23428
23429         // update child refs
23430         if(this.firstChild == node){
23431             this.setFirstChild(node.nextSibling);
23432         }
23433         if(this.lastChild == node){
23434             this.setLastChild(node.previousSibling);
23435         }
23436
23437         node.setOwnerTree(null);
23438         // clear any references from the node
23439         node.parentNode = null;
23440         node.previousSibling = null;
23441         node.nextSibling = null;
23442         this.fireEvent("remove", this.ownerTree, this, node);
23443         return node;
23444     },
23445
23446     /**
23447      * Inserts the first node before the second node in this nodes childNodes collection.
23448      * @param {Node} node The node to insert
23449      * @param {Node} refNode The node to insert before (if null the node is appended)
23450      * @return {Node} The inserted node
23451      */
23452     insertBefore : function(node, refNode){
23453         if(!refNode){ // like standard Dom, refNode can be null for append
23454             return this.appendChild(node);
23455         }
23456         // nothing to do
23457         if(node == refNode){
23458             return false;
23459         }
23460
23461         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23462             return false;
23463         }
23464         var index = this.childNodes.indexOf(refNode);
23465         var oldParent = node.parentNode;
23466         var refIndex = index;
23467
23468         // when moving internally, indexes will change after remove
23469         if(oldParent == this && this.childNodes.indexOf(node) < index){
23470             refIndex--;
23471         }
23472
23473         // it's a move, make sure we move it cleanly
23474         if(oldParent){
23475             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23476                 return false;
23477             }
23478             oldParent.removeChild(node);
23479         }
23480         if(refIndex == 0){
23481             this.setFirstChild(node);
23482         }
23483         this.childNodes.splice(refIndex, 0, node);
23484         node.parentNode = this;
23485         var ps = this.childNodes[refIndex-1];
23486         if(ps){
23487             node.previousSibling = ps;
23488             ps.nextSibling = node;
23489         }else{
23490             node.previousSibling = null;
23491         }
23492         node.nextSibling = refNode;
23493         refNode.previousSibling = node;
23494         node.setOwnerTree(this.getOwnerTree());
23495         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23496         if(oldParent){
23497             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23498         }
23499         return node;
23500     },
23501
23502     /**
23503      * Returns the child node at the specified index.
23504      * @param {Number} index
23505      * @return {Node}
23506      */
23507     item : function(index){
23508         return this.childNodes[index];
23509     },
23510
23511     /**
23512      * Replaces one child node in this node with another.
23513      * @param {Node} newChild The replacement node
23514      * @param {Node} oldChild The node to replace
23515      * @return {Node} The replaced node
23516      */
23517     replaceChild : function(newChild, oldChild){
23518         this.insertBefore(newChild, oldChild);
23519         this.removeChild(oldChild);
23520         return oldChild;
23521     },
23522
23523     /**
23524      * Returns the index of a child node
23525      * @param {Node} node
23526      * @return {Number} The index of the node or -1 if it was not found
23527      */
23528     indexOf : function(child){
23529         return this.childNodes.indexOf(child);
23530     },
23531
23532     /**
23533      * Returns the tree this node is in.
23534      * @return {Tree}
23535      */
23536     getOwnerTree : function(){
23537         // if it doesn't have one, look for one
23538         if(!this.ownerTree){
23539             var p = this;
23540             while(p){
23541                 if(p.ownerTree){
23542                     this.ownerTree = p.ownerTree;
23543                     break;
23544                 }
23545                 p = p.parentNode;
23546             }
23547         }
23548         return this.ownerTree;
23549     },
23550
23551     /**
23552      * Returns depth of this node (the root node has a depth of 0)
23553      * @return {Number}
23554      */
23555     getDepth : function(){
23556         var depth = 0;
23557         var p = this;
23558         while(p.parentNode){
23559             ++depth;
23560             p = p.parentNode;
23561         }
23562         return depth;
23563     },
23564
23565     // private
23566     setOwnerTree : function(tree){
23567         // if it's move, we need to update everyone
23568         if(tree != this.ownerTree){
23569             if(this.ownerTree){
23570                 this.ownerTree.unregisterNode(this);
23571             }
23572             this.ownerTree = tree;
23573             var cs = this.childNodes;
23574             for(var i = 0, len = cs.length; i < len; i++) {
23575                 cs[i].setOwnerTree(tree);
23576             }
23577             if(tree){
23578                 tree.registerNode(this);
23579             }
23580         }
23581     },
23582
23583     /**
23584      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23585      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23586      * @return {String} The path
23587      */
23588     getPath : function(attr){
23589         attr = attr || "id";
23590         var p = this.parentNode;
23591         var b = [this.attributes[attr]];
23592         while(p){
23593             b.unshift(p.attributes[attr]);
23594             p = p.parentNode;
23595         }
23596         var sep = this.getOwnerTree().pathSeparator;
23597         return sep + b.join(sep);
23598     },
23599
23600     /**
23601      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23602      * function call will be the scope provided or the current node. The arguments to the function
23603      * will be the args provided or the current node. If the function returns false at any point,
23604      * the bubble is stopped.
23605      * @param {Function} fn The function to call
23606      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23607      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23608      */
23609     bubble : function(fn, scope, args){
23610         var p = this;
23611         while(p){
23612             if(fn.call(scope || p, args || p) === false){
23613                 break;
23614             }
23615             p = p.parentNode;
23616         }
23617     },
23618
23619     /**
23620      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23621      * function call will be the scope provided or the current node. The arguments to the function
23622      * will be the args provided or the current node. If the function returns false at any point,
23623      * the cascade is stopped on that branch.
23624      * @param {Function} fn The function to call
23625      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23626      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23627      */
23628     cascade : function(fn, scope, args){
23629         if(fn.call(scope || this, args || this) !== false){
23630             var cs = this.childNodes;
23631             for(var i = 0, len = cs.length; i < len; i++) {
23632                 cs[i].cascade(fn, scope, args);
23633             }
23634         }
23635     },
23636
23637     /**
23638      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23639      * function call will be the scope provided or the current node. The arguments to the function
23640      * will be the args provided or the current node. If the function returns false at any point,
23641      * the iteration stops.
23642      * @param {Function} fn The function to call
23643      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23644      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23645      */
23646     eachChild : function(fn, scope, args){
23647         var cs = this.childNodes;
23648         for(var i = 0, len = cs.length; i < len; i++) {
23649                 if(fn.call(scope || this, args || cs[i]) === false){
23650                     break;
23651                 }
23652         }
23653     },
23654
23655     /**
23656      * Finds the first child that has the attribute with the specified value.
23657      * @param {String} attribute The attribute name
23658      * @param {Mixed} value The value to search for
23659      * @return {Node} The found child or null if none was found
23660      */
23661     findChild : function(attribute, value){
23662         var cs = this.childNodes;
23663         for(var i = 0, len = cs.length; i < len; i++) {
23664                 if(cs[i].attributes[attribute] == value){
23665                     return cs[i];
23666                 }
23667         }
23668         return null;
23669     },
23670
23671     /**
23672      * Finds the first child by a custom function. The child matches if the function passed
23673      * returns true.
23674      * @param {Function} fn
23675      * @param {Object} scope (optional)
23676      * @return {Node} The found child or null if none was found
23677      */
23678     findChildBy : function(fn, scope){
23679         var cs = this.childNodes;
23680         for(var i = 0, len = cs.length; i < len; i++) {
23681                 if(fn.call(scope||cs[i], cs[i]) === true){
23682                     return cs[i];
23683                 }
23684         }
23685         return null;
23686     },
23687
23688     /**
23689      * Sorts this nodes children using the supplied sort function
23690      * @param {Function} fn
23691      * @param {Object} scope (optional)
23692      */
23693     sort : function(fn, scope){
23694         var cs = this.childNodes;
23695         var len = cs.length;
23696         if(len > 0){
23697             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23698             cs.sort(sortFn);
23699             for(var i = 0; i < len; i++){
23700                 var n = cs[i];
23701                 n.previousSibling = cs[i-1];
23702                 n.nextSibling = cs[i+1];
23703                 if(i == 0){
23704                     this.setFirstChild(n);
23705                 }
23706                 if(i == len-1){
23707                     this.setLastChild(n);
23708                 }
23709             }
23710         }
23711     },
23712
23713     /**
23714      * Returns true if this node is an ancestor (at any point) of the passed node.
23715      * @param {Node} node
23716      * @return {Boolean}
23717      */
23718     contains : function(node){
23719         return node.isAncestor(this);
23720     },
23721
23722     /**
23723      * Returns true if the passed node is an ancestor (at any point) of this node.
23724      * @param {Node} node
23725      * @return {Boolean}
23726      */
23727     isAncestor : function(node){
23728         var p = this.parentNode;
23729         while(p){
23730             if(p == node){
23731                 return true;
23732             }
23733             p = p.parentNode;
23734         }
23735         return false;
23736     },
23737
23738     toString : function(){
23739         return "[Node"+(this.id?" "+this.id:"")+"]";
23740     }
23741 });/*
23742  * Based on:
23743  * Ext JS Library 1.1.1
23744  * Copyright(c) 2006-2007, Ext JS, LLC.
23745  *
23746  * Originally Released Under LGPL - original licence link has changed is not relivant.
23747  *
23748  * Fork - LGPL
23749  * <script type="text/javascript">
23750  */
23751  (function(){ 
23752 /**
23753  * @class Roo.Layer
23754  * @extends Roo.Element
23755  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23756  * automatic maintaining of shadow/shim positions.
23757  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23758  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23759  * you can pass a string with a CSS class name. False turns off the shadow.
23760  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23761  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23762  * @cfg {String} cls CSS class to add to the element
23763  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23764  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23765  * @constructor
23766  * @param {Object} config An object with config options.
23767  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23768  */
23769
23770 Roo.Layer = function(config, existingEl){
23771     config = config || {};
23772     var dh = Roo.DomHelper;
23773     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23774     if(existingEl){
23775         this.dom = Roo.getDom(existingEl);
23776     }
23777     if(!this.dom){
23778         var o = config.dh || {tag: "div", cls: "x-layer"};
23779         this.dom = dh.append(pel, o);
23780     }
23781     if(config.cls){
23782         this.addClass(config.cls);
23783     }
23784     this.constrain = config.constrain !== false;
23785     this.visibilityMode = Roo.Element.VISIBILITY;
23786     if(config.id){
23787         this.id = this.dom.id = config.id;
23788     }else{
23789         this.id = Roo.id(this.dom);
23790     }
23791     this.zindex = config.zindex || this.getZIndex();
23792     this.position("absolute", this.zindex);
23793     if(config.shadow){
23794         this.shadowOffset = config.shadowOffset || 4;
23795         this.shadow = new Roo.Shadow({
23796             offset : this.shadowOffset,
23797             mode : config.shadow
23798         });
23799     }else{
23800         this.shadowOffset = 0;
23801     }
23802     this.useShim = config.shim !== false && Roo.useShims;
23803     this.useDisplay = config.useDisplay;
23804     this.hide();
23805 };
23806
23807 var supr = Roo.Element.prototype;
23808
23809 // shims are shared among layer to keep from having 100 iframes
23810 var shims = [];
23811
23812 Roo.extend(Roo.Layer, Roo.Element, {
23813
23814     getZIndex : function(){
23815         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23816     },
23817
23818     getShim : function(){
23819         if(!this.useShim){
23820             return null;
23821         }
23822         if(this.shim){
23823             return this.shim;
23824         }
23825         var shim = shims.shift();
23826         if(!shim){
23827             shim = this.createShim();
23828             shim.enableDisplayMode('block');
23829             shim.dom.style.display = 'none';
23830             shim.dom.style.visibility = 'visible';
23831         }
23832         var pn = this.dom.parentNode;
23833         if(shim.dom.parentNode != pn){
23834             pn.insertBefore(shim.dom, this.dom);
23835         }
23836         shim.setStyle('z-index', this.getZIndex()-2);
23837         this.shim = shim;
23838         return shim;
23839     },
23840
23841     hideShim : function(){
23842         if(this.shim){
23843             this.shim.setDisplayed(false);
23844             shims.push(this.shim);
23845             delete this.shim;
23846         }
23847     },
23848
23849     disableShadow : function(){
23850         if(this.shadow){
23851             this.shadowDisabled = true;
23852             this.shadow.hide();
23853             this.lastShadowOffset = this.shadowOffset;
23854             this.shadowOffset = 0;
23855         }
23856     },
23857
23858     enableShadow : function(show){
23859         if(this.shadow){
23860             this.shadowDisabled = false;
23861             this.shadowOffset = this.lastShadowOffset;
23862             delete this.lastShadowOffset;
23863             if(show){
23864                 this.sync(true);
23865             }
23866         }
23867     },
23868
23869     // private
23870     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23871     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23872     sync : function(doShow){
23873         var sw = this.shadow;
23874         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23875             var sh = this.getShim();
23876
23877             var w = this.getWidth(),
23878                 h = this.getHeight();
23879
23880             var l = this.getLeft(true),
23881                 t = this.getTop(true);
23882
23883             if(sw && !this.shadowDisabled){
23884                 if(doShow && !sw.isVisible()){
23885                     sw.show(this);
23886                 }else{
23887                     sw.realign(l, t, w, h);
23888                 }
23889                 if(sh){
23890                     if(doShow){
23891                        sh.show();
23892                     }
23893                     // fit the shim behind the shadow, so it is shimmed too
23894                     var a = sw.adjusts, s = sh.dom.style;
23895                     s.left = (Math.min(l, l+a.l))+"px";
23896                     s.top = (Math.min(t, t+a.t))+"px";
23897                     s.width = (w+a.w)+"px";
23898                     s.height = (h+a.h)+"px";
23899                 }
23900             }else if(sh){
23901                 if(doShow){
23902                    sh.show();
23903                 }
23904                 sh.setSize(w, h);
23905                 sh.setLeftTop(l, t);
23906             }
23907             
23908         }
23909     },
23910
23911     // private
23912     destroy : function(){
23913         this.hideShim();
23914         if(this.shadow){
23915             this.shadow.hide();
23916         }
23917         this.removeAllListeners();
23918         var pn = this.dom.parentNode;
23919         if(pn){
23920             pn.removeChild(this.dom);
23921         }
23922         Roo.Element.uncache(this.id);
23923     },
23924
23925     remove : function(){
23926         this.destroy();
23927     },
23928
23929     // private
23930     beginUpdate : function(){
23931         this.updating = true;
23932     },
23933
23934     // private
23935     endUpdate : function(){
23936         this.updating = false;
23937         this.sync(true);
23938     },
23939
23940     // private
23941     hideUnders : function(negOffset){
23942         if(this.shadow){
23943             this.shadow.hide();
23944         }
23945         this.hideShim();
23946     },
23947
23948     // private
23949     constrainXY : function(){
23950         if(this.constrain){
23951             var vw = Roo.lib.Dom.getViewWidth(),
23952                 vh = Roo.lib.Dom.getViewHeight();
23953             var s = Roo.get(document).getScroll();
23954
23955             var xy = this.getXY();
23956             var x = xy[0], y = xy[1];   
23957             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23958             // only move it if it needs it
23959             var moved = false;
23960             // first validate right/bottom
23961             if((x + w) > vw+s.left){
23962                 x = vw - w - this.shadowOffset;
23963                 moved = true;
23964             }
23965             if((y + h) > vh+s.top){
23966                 y = vh - h - this.shadowOffset;
23967                 moved = true;
23968             }
23969             // then make sure top/left isn't negative
23970             if(x < s.left){
23971                 x = s.left;
23972                 moved = true;
23973             }
23974             if(y < s.top){
23975                 y = s.top;
23976                 moved = true;
23977             }
23978             if(moved){
23979                 if(this.avoidY){
23980                     var ay = this.avoidY;
23981                     if(y <= ay && (y+h) >= ay){
23982                         y = ay-h-5;   
23983                     }
23984                 }
23985                 xy = [x, y];
23986                 this.storeXY(xy);
23987                 supr.setXY.call(this, xy);
23988                 this.sync();
23989             }
23990         }
23991     },
23992
23993     isVisible : function(){
23994         return this.visible;    
23995     },
23996
23997     // private
23998     showAction : function(){
23999         this.visible = true; // track visibility to prevent getStyle calls
24000         if(this.useDisplay === true){
24001             this.setDisplayed("");
24002         }else if(this.lastXY){
24003             supr.setXY.call(this, this.lastXY);
24004         }else if(this.lastLT){
24005             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
24006         }
24007     },
24008
24009     // private
24010     hideAction : function(){
24011         this.visible = false;
24012         if(this.useDisplay === true){
24013             this.setDisplayed(false);
24014         }else{
24015             this.setLeftTop(-10000,-10000);
24016         }
24017     },
24018
24019     // overridden Element method
24020     setVisible : function(v, a, d, c, e){
24021         if(v){
24022             this.showAction();
24023         }
24024         if(a && v){
24025             var cb = function(){
24026                 this.sync(true);
24027                 if(c){
24028                     c();
24029                 }
24030             }.createDelegate(this);
24031             supr.setVisible.call(this, true, true, d, cb, e);
24032         }else{
24033             if(!v){
24034                 this.hideUnders(true);
24035             }
24036             var cb = c;
24037             if(a){
24038                 cb = function(){
24039                     this.hideAction();
24040                     if(c){
24041                         c();
24042                     }
24043                 }.createDelegate(this);
24044             }
24045             supr.setVisible.call(this, v, a, d, cb, e);
24046             if(v){
24047                 this.sync(true);
24048             }else if(!a){
24049                 this.hideAction();
24050             }
24051         }
24052     },
24053
24054     storeXY : function(xy){
24055         delete this.lastLT;
24056         this.lastXY = xy;
24057     },
24058
24059     storeLeftTop : function(left, top){
24060         delete this.lastXY;
24061         this.lastLT = [left, top];
24062     },
24063
24064     // private
24065     beforeFx : function(){
24066         this.beforeAction();
24067         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24068     },
24069
24070     // private
24071     afterFx : function(){
24072         Roo.Layer.superclass.afterFx.apply(this, arguments);
24073         this.sync(this.isVisible());
24074     },
24075
24076     // private
24077     beforeAction : function(){
24078         if(!this.updating && this.shadow){
24079             this.shadow.hide();
24080         }
24081     },
24082
24083     // overridden Element method
24084     setLeft : function(left){
24085         this.storeLeftTop(left, this.getTop(true));
24086         supr.setLeft.apply(this, arguments);
24087         this.sync();
24088     },
24089
24090     setTop : function(top){
24091         this.storeLeftTop(this.getLeft(true), top);
24092         supr.setTop.apply(this, arguments);
24093         this.sync();
24094     },
24095
24096     setLeftTop : function(left, top){
24097         this.storeLeftTop(left, top);
24098         supr.setLeftTop.apply(this, arguments);
24099         this.sync();
24100     },
24101
24102     setXY : function(xy, a, d, c, e){
24103         this.fixDisplay();
24104         this.beforeAction();
24105         this.storeXY(xy);
24106         var cb = this.createCB(c);
24107         supr.setXY.call(this, xy, a, d, cb, e);
24108         if(!a){
24109             cb();
24110         }
24111     },
24112
24113     // private
24114     createCB : function(c){
24115         var el = this;
24116         return function(){
24117             el.constrainXY();
24118             el.sync(true);
24119             if(c){
24120                 c();
24121             }
24122         };
24123     },
24124
24125     // overridden Element method
24126     setX : function(x, a, d, c, e){
24127         this.setXY([x, this.getY()], a, d, c, e);
24128     },
24129
24130     // overridden Element method
24131     setY : function(y, a, d, c, e){
24132         this.setXY([this.getX(), y], a, d, c, e);
24133     },
24134
24135     // overridden Element method
24136     setSize : function(w, h, a, d, c, e){
24137         this.beforeAction();
24138         var cb = this.createCB(c);
24139         supr.setSize.call(this, w, h, a, d, cb, e);
24140         if(!a){
24141             cb();
24142         }
24143     },
24144
24145     // overridden Element method
24146     setWidth : function(w, a, d, c, e){
24147         this.beforeAction();
24148         var cb = this.createCB(c);
24149         supr.setWidth.call(this, w, a, d, cb, e);
24150         if(!a){
24151             cb();
24152         }
24153     },
24154
24155     // overridden Element method
24156     setHeight : function(h, a, d, c, e){
24157         this.beforeAction();
24158         var cb = this.createCB(c);
24159         supr.setHeight.call(this, h, a, d, cb, e);
24160         if(!a){
24161             cb();
24162         }
24163     },
24164
24165     // overridden Element method
24166     setBounds : function(x, y, w, h, a, d, c, e){
24167         this.beforeAction();
24168         var cb = this.createCB(c);
24169         if(!a){
24170             this.storeXY([x, y]);
24171             supr.setXY.call(this, [x, y]);
24172             supr.setSize.call(this, w, h, a, d, cb, e);
24173             cb();
24174         }else{
24175             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24176         }
24177         return this;
24178     },
24179     
24180     /**
24181      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24182      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24183      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24184      * @param {Number} zindex The new z-index to set
24185      * @return {this} The Layer
24186      */
24187     setZIndex : function(zindex){
24188         this.zindex = zindex;
24189         this.setStyle("z-index", zindex + 2);
24190         if(this.shadow){
24191             this.shadow.setZIndex(zindex + 1);
24192         }
24193         if(this.shim){
24194             this.shim.setStyle("z-index", zindex);
24195         }
24196     }
24197 });
24198 })();/*
24199  * Based on:
24200  * Ext JS Library 1.1.1
24201  * Copyright(c) 2006-2007, Ext JS, LLC.
24202  *
24203  * Originally Released Under LGPL - original licence link has changed is not relivant.
24204  *
24205  * Fork - LGPL
24206  * <script type="text/javascript">
24207  */
24208
24209
24210 /**
24211  * @class Roo.Shadow
24212  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24213  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24214  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24215  * @constructor
24216  * Create a new Shadow
24217  * @param {Object} config The config object
24218  */
24219 Roo.Shadow = function(config){
24220     Roo.apply(this, config);
24221     if(typeof this.mode != "string"){
24222         this.mode = this.defaultMode;
24223     }
24224     var o = this.offset, a = {h: 0};
24225     var rad = Math.floor(this.offset/2);
24226     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24227         case "drop":
24228             a.w = 0;
24229             a.l = a.t = o;
24230             a.t -= 1;
24231             if(Roo.isIE){
24232                 a.l -= this.offset + rad;
24233                 a.t -= this.offset + rad;
24234                 a.w -= rad;
24235                 a.h -= rad;
24236                 a.t += 1;
24237             }
24238         break;
24239         case "sides":
24240             a.w = (o*2);
24241             a.l = -o;
24242             a.t = o-1;
24243             if(Roo.isIE){
24244                 a.l -= (this.offset - rad);
24245                 a.t -= this.offset + rad;
24246                 a.l += 1;
24247                 a.w -= (this.offset - rad)*2;
24248                 a.w -= rad + 1;
24249                 a.h -= 1;
24250             }
24251         break;
24252         case "frame":
24253             a.w = a.h = (o*2);
24254             a.l = a.t = -o;
24255             a.t += 1;
24256             a.h -= 2;
24257             if(Roo.isIE){
24258                 a.l -= (this.offset - rad);
24259                 a.t -= (this.offset - rad);
24260                 a.l += 1;
24261                 a.w -= (this.offset + rad + 1);
24262                 a.h -= (this.offset + rad);
24263                 a.h += 1;
24264             }
24265         break;
24266     };
24267
24268     this.adjusts = a;
24269 };
24270
24271 Roo.Shadow.prototype = {
24272     /**
24273      * @cfg {String} mode
24274      * The shadow display mode.  Supports the following options:<br />
24275      * sides: Shadow displays on both sides and bottom only<br />
24276      * frame: Shadow displays equally on all four sides<br />
24277      * drop: Traditional bottom-right drop shadow (default)
24278      */
24279     /**
24280      * @cfg {String} offset
24281      * The number of pixels to offset the shadow from the element (defaults to 4)
24282      */
24283     offset: 4,
24284
24285     // private
24286     defaultMode: "drop",
24287
24288     /**
24289      * Displays the shadow under the target element
24290      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24291      */
24292     show : function(target){
24293         target = Roo.get(target);
24294         if(!this.el){
24295             this.el = Roo.Shadow.Pool.pull();
24296             if(this.el.dom.nextSibling != target.dom){
24297                 this.el.insertBefore(target);
24298             }
24299         }
24300         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24301         if(Roo.isIE){
24302             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24303         }
24304         this.realign(
24305             target.getLeft(true),
24306             target.getTop(true),
24307             target.getWidth(),
24308             target.getHeight()
24309         );
24310         this.el.dom.style.display = "block";
24311     },
24312
24313     /**
24314      * Returns true if the shadow is visible, else false
24315      */
24316     isVisible : function(){
24317         return this.el ? true : false;  
24318     },
24319
24320     /**
24321      * Direct alignment when values are already available. Show must be called at least once before
24322      * calling this method to ensure it is initialized.
24323      * @param {Number} left The target element left position
24324      * @param {Number} top The target element top position
24325      * @param {Number} width The target element width
24326      * @param {Number} height The target element height
24327      */
24328     realign : function(l, t, w, h){
24329         if(!this.el){
24330             return;
24331         }
24332         var a = this.adjusts, d = this.el.dom, s = d.style;
24333         var iea = 0;
24334         s.left = (l+a.l)+"px";
24335         s.top = (t+a.t)+"px";
24336         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24337  
24338         if(s.width != sws || s.height != shs){
24339             s.width = sws;
24340             s.height = shs;
24341             if(!Roo.isIE){
24342                 var cn = d.childNodes;
24343                 var sww = Math.max(0, (sw-12))+"px";
24344                 cn[0].childNodes[1].style.width = sww;
24345                 cn[1].childNodes[1].style.width = sww;
24346                 cn[2].childNodes[1].style.width = sww;
24347                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24348             }
24349         }
24350     },
24351
24352     /**
24353      * Hides this shadow
24354      */
24355     hide : function(){
24356         if(this.el){
24357             this.el.dom.style.display = "none";
24358             Roo.Shadow.Pool.push(this.el);
24359             delete this.el;
24360         }
24361     },
24362
24363     /**
24364      * Adjust the z-index of this shadow
24365      * @param {Number} zindex The new z-index
24366      */
24367     setZIndex : function(z){
24368         this.zIndex = z;
24369         if(this.el){
24370             this.el.setStyle("z-index", z);
24371         }
24372     }
24373 };
24374
24375 // Private utility class that manages the internal Shadow cache
24376 Roo.Shadow.Pool = function(){
24377     var p = [];
24378     var markup = Roo.isIE ?
24379                  '<div class="x-ie-shadow"></div>' :
24380                  '<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>';
24381     return {
24382         pull : function(){
24383             var sh = p.shift();
24384             if(!sh){
24385                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24386                 sh.autoBoxAdjust = false;
24387             }
24388             return sh;
24389         },
24390
24391         push : function(sh){
24392             p.push(sh);
24393         }
24394     };
24395 }();/*
24396  * Based on:
24397  * Ext JS Library 1.1.1
24398  * Copyright(c) 2006-2007, Ext JS, LLC.
24399  *
24400  * Originally Released Under LGPL - original licence link has changed is not relivant.
24401  *
24402  * Fork - LGPL
24403  * <script type="text/javascript">
24404  */
24405
24406
24407 /**
24408  * @class Roo.SplitBar
24409  * @extends Roo.util.Observable
24410  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24411  * <br><br>
24412  * Usage:
24413  * <pre><code>
24414 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24415                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24416 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24417 split.minSize = 100;
24418 split.maxSize = 600;
24419 split.animate = true;
24420 split.on('moved', splitterMoved);
24421 </code></pre>
24422  * @constructor
24423  * Create a new SplitBar
24424  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24425  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24426  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24427  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24428                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24429                         position of the SplitBar).
24430  */
24431 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24432     
24433     /** @private */
24434     this.el = Roo.get(dragElement, true);
24435     this.el.dom.unselectable = "on";
24436     /** @private */
24437     this.resizingEl = Roo.get(resizingElement, true);
24438
24439     /**
24440      * @private
24441      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24442      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24443      * @type Number
24444      */
24445     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24446     
24447     /**
24448      * The minimum size of the resizing element. (Defaults to 0)
24449      * @type Number
24450      */
24451     this.minSize = 0;
24452     
24453     /**
24454      * The maximum size of the resizing element. (Defaults to 2000)
24455      * @type Number
24456      */
24457     this.maxSize = 2000;
24458     
24459     /**
24460      * Whether to animate the transition to the new size
24461      * @type Boolean
24462      */
24463     this.animate = false;
24464     
24465     /**
24466      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24467      * @type Boolean
24468      */
24469     this.useShim = false;
24470     
24471     /** @private */
24472     this.shim = null;
24473     
24474     if(!existingProxy){
24475         /** @private */
24476         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24477     }else{
24478         this.proxy = Roo.get(existingProxy).dom;
24479     }
24480     /** @private */
24481     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24482     
24483     /** @private */
24484     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24485     
24486     /** @private */
24487     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24488     
24489     /** @private */
24490     this.dragSpecs = {};
24491     
24492     /**
24493      * @private The adapter to use to positon and resize elements
24494      */
24495     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24496     this.adapter.init(this);
24497     
24498     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24499         /** @private */
24500         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24501         this.el.addClass("x-splitbar-h");
24502     }else{
24503         /** @private */
24504         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24505         this.el.addClass("x-splitbar-v");
24506     }
24507     
24508     this.addEvents({
24509         /**
24510          * @event resize
24511          * Fires when the splitter is moved (alias for {@link #event-moved})
24512          * @param {Roo.SplitBar} this
24513          * @param {Number} newSize the new width or height
24514          */
24515         "resize" : true,
24516         /**
24517          * @event moved
24518          * Fires when the splitter is moved
24519          * @param {Roo.SplitBar} this
24520          * @param {Number} newSize the new width or height
24521          */
24522         "moved" : true,
24523         /**
24524          * @event beforeresize
24525          * Fires before the splitter is dragged
24526          * @param {Roo.SplitBar} this
24527          */
24528         "beforeresize" : true,
24529
24530         "beforeapply" : true
24531     });
24532
24533     Roo.util.Observable.call(this);
24534 };
24535
24536 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24537     onStartProxyDrag : function(x, y){
24538         this.fireEvent("beforeresize", this);
24539         if(!this.overlay){
24540             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24541             o.unselectable();
24542             o.enableDisplayMode("block");
24543             // all splitbars share the same overlay
24544             Roo.SplitBar.prototype.overlay = o;
24545         }
24546         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24547         this.overlay.show();
24548         Roo.get(this.proxy).setDisplayed("block");
24549         var size = this.adapter.getElementSize(this);
24550         this.activeMinSize = this.getMinimumSize();;
24551         this.activeMaxSize = this.getMaximumSize();;
24552         var c1 = size - this.activeMinSize;
24553         var c2 = Math.max(this.activeMaxSize - size, 0);
24554         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24555             this.dd.resetConstraints();
24556             this.dd.setXConstraint(
24557                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24558                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24559             );
24560             this.dd.setYConstraint(0, 0);
24561         }else{
24562             this.dd.resetConstraints();
24563             this.dd.setXConstraint(0, 0);
24564             this.dd.setYConstraint(
24565                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24566                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24567             );
24568          }
24569         this.dragSpecs.startSize = size;
24570         this.dragSpecs.startPoint = [x, y];
24571         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24572     },
24573     
24574     /** 
24575      * @private Called after the drag operation by the DDProxy
24576      */
24577     onEndProxyDrag : function(e){
24578         Roo.get(this.proxy).setDisplayed(false);
24579         var endPoint = Roo.lib.Event.getXY(e);
24580         if(this.overlay){
24581             this.overlay.hide();
24582         }
24583         var newSize;
24584         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24585             newSize = this.dragSpecs.startSize + 
24586                 (this.placement == Roo.SplitBar.LEFT ?
24587                     endPoint[0] - this.dragSpecs.startPoint[0] :
24588                     this.dragSpecs.startPoint[0] - endPoint[0]
24589                 );
24590         }else{
24591             newSize = this.dragSpecs.startSize + 
24592                 (this.placement == Roo.SplitBar.TOP ?
24593                     endPoint[1] - this.dragSpecs.startPoint[1] :
24594                     this.dragSpecs.startPoint[1] - endPoint[1]
24595                 );
24596         }
24597         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24598         if(newSize != this.dragSpecs.startSize){
24599             if(this.fireEvent('beforeapply', this, newSize) !== false){
24600                 this.adapter.setElementSize(this, newSize);
24601                 this.fireEvent("moved", this, newSize);
24602                 this.fireEvent("resize", this, newSize);
24603             }
24604         }
24605     },
24606     
24607     /**
24608      * Get the adapter this SplitBar uses
24609      * @return The adapter object
24610      */
24611     getAdapter : function(){
24612         return this.adapter;
24613     },
24614     
24615     /**
24616      * Set the adapter this SplitBar uses
24617      * @param {Object} adapter A SplitBar adapter object
24618      */
24619     setAdapter : function(adapter){
24620         this.adapter = adapter;
24621         this.adapter.init(this);
24622     },
24623     
24624     /**
24625      * Gets the minimum size for the resizing element
24626      * @return {Number} The minimum size
24627      */
24628     getMinimumSize : function(){
24629         return this.minSize;
24630     },
24631     
24632     /**
24633      * Sets the minimum size for the resizing element
24634      * @param {Number} minSize The minimum size
24635      */
24636     setMinimumSize : function(minSize){
24637         this.minSize = minSize;
24638     },
24639     
24640     /**
24641      * Gets the maximum size for the resizing element
24642      * @return {Number} The maximum size
24643      */
24644     getMaximumSize : function(){
24645         return this.maxSize;
24646     },
24647     
24648     /**
24649      * Sets the maximum size for the resizing element
24650      * @param {Number} maxSize The maximum size
24651      */
24652     setMaximumSize : function(maxSize){
24653         this.maxSize = maxSize;
24654     },
24655     
24656     /**
24657      * Sets the initialize size for the resizing element
24658      * @param {Number} size The initial size
24659      */
24660     setCurrentSize : function(size){
24661         var oldAnimate = this.animate;
24662         this.animate = false;
24663         this.adapter.setElementSize(this, size);
24664         this.animate = oldAnimate;
24665     },
24666     
24667     /**
24668      * Destroy this splitbar. 
24669      * @param {Boolean} removeEl True to remove the element
24670      */
24671     destroy : function(removeEl){
24672         if(this.shim){
24673             this.shim.remove();
24674         }
24675         this.dd.unreg();
24676         this.proxy.parentNode.removeChild(this.proxy);
24677         if(removeEl){
24678             this.el.remove();
24679         }
24680     }
24681 });
24682
24683 /**
24684  * @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.
24685  */
24686 Roo.SplitBar.createProxy = function(dir){
24687     var proxy = new Roo.Element(document.createElement("div"));
24688     proxy.unselectable();
24689     var cls = 'x-splitbar-proxy';
24690     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24691     document.body.appendChild(proxy.dom);
24692     return proxy.dom;
24693 };
24694
24695 /** 
24696  * @class Roo.SplitBar.BasicLayoutAdapter
24697  * Default Adapter. It assumes the splitter and resizing element are not positioned
24698  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24699  */
24700 Roo.SplitBar.BasicLayoutAdapter = function(){
24701 };
24702
24703 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24704     // do nothing for now
24705     init : function(s){
24706     
24707     },
24708     /**
24709      * Called before drag operations to get the current size of the resizing element. 
24710      * @param {Roo.SplitBar} s The SplitBar using this adapter
24711      */
24712      getElementSize : function(s){
24713         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24714             return s.resizingEl.getWidth();
24715         }else{
24716             return s.resizingEl.getHeight();
24717         }
24718     },
24719     
24720     /**
24721      * Called after drag operations to set the size of the resizing element.
24722      * @param {Roo.SplitBar} s The SplitBar using this adapter
24723      * @param {Number} newSize The new size to set
24724      * @param {Function} onComplete A function to be invoked when resizing is complete
24725      */
24726     setElementSize : function(s, newSize, onComplete){
24727         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24728             if(!s.animate){
24729                 s.resizingEl.setWidth(newSize);
24730                 if(onComplete){
24731                     onComplete(s, newSize);
24732                 }
24733             }else{
24734                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24735             }
24736         }else{
24737             
24738             if(!s.animate){
24739                 s.resizingEl.setHeight(newSize);
24740                 if(onComplete){
24741                     onComplete(s, newSize);
24742                 }
24743             }else{
24744                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24745             }
24746         }
24747     }
24748 };
24749
24750 /** 
24751  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24752  * @extends Roo.SplitBar.BasicLayoutAdapter
24753  * Adapter that  moves the splitter element to align with the resized sizing element. 
24754  * Used with an absolute positioned SplitBar.
24755  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24756  * document.body, make sure you assign an id to the body element.
24757  */
24758 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24759     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24760     this.container = Roo.get(container);
24761 };
24762
24763 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24764     init : function(s){
24765         this.basic.init(s);
24766     },
24767     
24768     getElementSize : function(s){
24769         return this.basic.getElementSize(s);
24770     },
24771     
24772     setElementSize : function(s, newSize, onComplete){
24773         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24774     },
24775     
24776     moveSplitter : function(s){
24777         var yes = Roo.SplitBar;
24778         switch(s.placement){
24779             case yes.LEFT:
24780                 s.el.setX(s.resizingEl.getRight());
24781                 break;
24782             case yes.RIGHT:
24783                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24784                 break;
24785             case yes.TOP:
24786                 s.el.setY(s.resizingEl.getBottom());
24787                 break;
24788             case yes.BOTTOM:
24789                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24790                 break;
24791         }
24792     }
24793 };
24794
24795 /**
24796  * Orientation constant - Create a vertical SplitBar
24797  * @static
24798  * @type Number
24799  */
24800 Roo.SplitBar.VERTICAL = 1;
24801
24802 /**
24803  * Orientation constant - Create a horizontal SplitBar
24804  * @static
24805  * @type Number
24806  */
24807 Roo.SplitBar.HORIZONTAL = 2;
24808
24809 /**
24810  * Placement constant - The resizing element is to the left of the splitter element
24811  * @static
24812  * @type Number
24813  */
24814 Roo.SplitBar.LEFT = 1;
24815
24816 /**
24817  * Placement constant - The resizing element is to the right of the splitter element
24818  * @static
24819  * @type Number
24820  */
24821 Roo.SplitBar.RIGHT = 2;
24822
24823 /**
24824  * Placement constant - The resizing element is positioned above the splitter element
24825  * @static
24826  * @type Number
24827  */
24828 Roo.SplitBar.TOP = 3;
24829
24830 /**
24831  * Placement constant - The resizing element is positioned under splitter element
24832  * @static
24833  * @type Number
24834  */
24835 Roo.SplitBar.BOTTOM = 4;
24836 /*
24837  * Based on:
24838  * Ext JS Library 1.1.1
24839  * Copyright(c) 2006-2007, Ext JS, LLC.
24840  *
24841  * Originally Released Under LGPL - original licence link has changed is not relivant.
24842  *
24843  * Fork - LGPL
24844  * <script type="text/javascript">
24845  */
24846
24847 /**
24848  * @class Roo.View
24849  * @extends Roo.util.Observable
24850  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24851  * This class also supports single and multi selection modes. <br>
24852  * Create a data model bound view:
24853  <pre><code>
24854  var store = new Roo.data.Store(...);
24855
24856  var view = new Roo.View({
24857     el : "my-element",
24858     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24859  
24860     singleSelect: true,
24861     selectedClass: "ydataview-selected",
24862     store: store
24863  });
24864
24865  // listen for node click?
24866  view.on("click", function(vw, index, node, e){
24867  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24868  });
24869
24870  // load XML data
24871  dataModel.load("foobar.xml");
24872  </code></pre>
24873  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24874  * <br><br>
24875  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24876  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24877  * 
24878  * Note: old style constructor is still suported (container, template, config)
24879  * 
24880  * @constructor
24881  * Create a new View
24882  * @param {Object} config The config object
24883  * 
24884  */
24885 Roo.View = function(config, depreciated_tpl, depreciated_config){
24886     
24887     this.parent = false;
24888     
24889     if (typeof(depreciated_tpl) == 'undefined') {
24890         // new way.. - universal constructor.
24891         Roo.apply(this, config);
24892         this.el  = Roo.get(this.el);
24893     } else {
24894         // old format..
24895         this.el  = Roo.get(config);
24896         this.tpl = depreciated_tpl;
24897         Roo.apply(this, depreciated_config);
24898     }
24899     this.wrapEl  = this.el.wrap().wrap();
24900     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24901     
24902     
24903     if(typeof(this.tpl) == "string"){
24904         this.tpl = new Roo.Template(this.tpl);
24905     } else {
24906         // support xtype ctors..
24907         this.tpl = new Roo.factory(this.tpl, Roo);
24908     }
24909     
24910     
24911     this.tpl.compile();
24912     
24913     /** @private */
24914     this.addEvents({
24915         /**
24916          * @event beforeclick
24917          * Fires before a click is processed. Returns false to cancel the default action.
24918          * @param {Roo.View} this
24919          * @param {Number} index The index of the target node
24920          * @param {HTMLElement} node The target node
24921          * @param {Roo.EventObject} e The raw event object
24922          */
24923             "beforeclick" : true,
24924         /**
24925          * @event click
24926          * Fires when a template node is clicked.
24927          * @param {Roo.View} this
24928          * @param {Number} index The index of the target node
24929          * @param {HTMLElement} node The target node
24930          * @param {Roo.EventObject} e The raw event object
24931          */
24932             "click" : true,
24933         /**
24934          * @event dblclick
24935          * Fires when a template node is double clicked.
24936          * @param {Roo.View} this
24937          * @param {Number} index The index of the target node
24938          * @param {HTMLElement} node The target node
24939          * @param {Roo.EventObject} e The raw event object
24940          */
24941             "dblclick" : true,
24942         /**
24943          * @event contextmenu
24944          * Fires when a template node is right clicked.
24945          * @param {Roo.View} this
24946          * @param {Number} index The index of the target node
24947          * @param {HTMLElement} node The target node
24948          * @param {Roo.EventObject} e The raw event object
24949          */
24950             "contextmenu" : true,
24951         /**
24952          * @event selectionchange
24953          * Fires when the selected nodes change.
24954          * @param {Roo.View} this
24955          * @param {Array} selections Array of the selected nodes
24956          */
24957             "selectionchange" : true,
24958     
24959         /**
24960          * @event beforeselect
24961          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24962          * @param {Roo.View} this
24963          * @param {HTMLElement} node The node to be selected
24964          * @param {Array} selections Array of currently selected nodes
24965          */
24966             "beforeselect" : true,
24967         /**
24968          * @event preparedata
24969          * Fires on every row to render, to allow you to change the data.
24970          * @param {Roo.View} this
24971          * @param {Object} data to be rendered (change this)
24972          */
24973           "preparedata" : true
24974           
24975           
24976         });
24977
24978
24979
24980     this.el.on({
24981         "click": this.onClick,
24982         "dblclick": this.onDblClick,
24983         "contextmenu": this.onContextMenu,
24984         scope:this
24985     });
24986
24987     this.selections = [];
24988     this.nodes = [];
24989     this.cmp = new Roo.CompositeElementLite([]);
24990     if(this.store){
24991         this.store = Roo.factory(this.store, Roo.data);
24992         this.setStore(this.store, true);
24993     }
24994     
24995     if ( this.footer && this.footer.xtype) {
24996            
24997          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24998         
24999         this.footer.dataSource = this.store
25000         this.footer.container = fctr;
25001         this.footer = Roo.factory(this.footer, Roo);
25002         fctr.insertFirst(this.el);
25003         
25004         // this is a bit insane - as the paging toolbar seems to detach the el..
25005 //        dom.parentNode.parentNode.parentNode
25006          // they get detached?
25007     }
25008     
25009     
25010     Roo.View.superclass.constructor.call(this);
25011     
25012     
25013 };
25014
25015 Roo.extend(Roo.View, Roo.util.Observable, {
25016     
25017      /**
25018      * @cfg {Roo.data.Store} store Data store to load data from.
25019      */
25020     store : false,
25021     
25022     /**
25023      * @cfg {String|Roo.Element} el The container element.
25024      */
25025     el : '',
25026     
25027     /**
25028      * @cfg {String|Roo.Template} tpl The template used by this View 
25029      */
25030     tpl : false,
25031     /**
25032      * @cfg {String} dataName the named area of the template to use as the data area
25033      *                          Works with domtemplates roo-name="name"
25034      */
25035     dataName: false,
25036     /**
25037      * @cfg {String} selectedClass The css class to add to selected nodes
25038      */
25039     selectedClass : "x-view-selected",
25040      /**
25041      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25042      */
25043     emptyText : "",
25044     
25045     /**
25046      * @cfg {String} text to display on mask (default Loading)
25047      */
25048     mask : false,
25049     /**
25050      * @cfg {Boolean} multiSelect Allow multiple selection
25051      */
25052     multiSelect : false,
25053     /**
25054      * @cfg {Boolean} singleSelect Allow single selection
25055      */
25056     singleSelect:  false,
25057     
25058     /**
25059      * @cfg {Boolean} toggleSelect - selecting 
25060      */
25061     toggleSelect : false,
25062     
25063     /**
25064      * @cfg {Boolean} tickable - selecting 
25065      */
25066     tickable : false,
25067     
25068     /**
25069      * Returns the element this view is bound to.
25070      * @return {Roo.Element}
25071      */
25072     getEl : function(){
25073         return this.wrapEl;
25074     },
25075     
25076     
25077
25078     /**
25079      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25080      */
25081     refresh : function(){
25082         //Roo.log('refresh');
25083         var t = this.tpl;
25084         
25085         // if we are using something like 'domtemplate', then
25086         // the what gets used is:
25087         // t.applySubtemplate(NAME, data, wrapping data..)
25088         // the outer template then get' applied with
25089         //     the store 'extra data'
25090         // and the body get's added to the
25091         //      roo-name="data" node?
25092         //      <span class='roo-tpl-{name}'></span> ?????
25093         
25094         
25095         
25096         this.clearSelections();
25097         this.el.update("");
25098         var html = [];
25099         var records = this.store.getRange();
25100         if(records.length < 1) {
25101             
25102             // is this valid??  = should it render a template??
25103             
25104             this.el.update(this.emptyText);
25105             return;
25106         }
25107         var el = this.el;
25108         if (this.dataName) {
25109             this.el.update(t.apply(this.store.meta)); //????
25110             el = this.el.child('.roo-tpl-' + this.dataName);
25111         }
25112         
25113         for(var i = 0, len = records.length; i < len; i++){
25114             var data = this.prepareData(records[i].data, i, records[i]);
25115             this.fireEvent("preparedata", this, data, i, records[i]);
25116             
25117             var d = Roo.apply({}, data);
25118             
25119             if(this.tickable){
25120                 Roo.apply(d, {'roo-id' : Roo.id()});
25121                 
25122                 var _this = this;
25123             
25124                 Roo.each(this.parent.item, function(item){
25125                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25126                         return;
25127                     }
25128                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25129                 });
25130             }
25131             
25132             html[html.length] = Roo.util.Format.trim(
25133                 this.dataName ?
25134                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25135                     t.apply(d)
25136             );
25137         }
25138         
25139         
25140         
25141         el.update(html.join(""));
25142         this.nodes = el.dom.childNodes;
25143         this.updateIndexes(0);
25144     },
25145     
25146
25147     /**
25148      * Function to override to reformat the data that is sent to
25149      * the template for each node.
25150      * DEPRICATED - use the preparedata event handler.
25151      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25152      * a JSON object for an UpdateManager bound view).
25153      */
25154     prepareData : function(data, index, record)
25155     {
25156         this.fireEvent("preparedata", this, data, index, record);
25157         return data;
25158     },
25159
25160     onUpdate : function(ds, record){
25161         // Roo.log('on update');   
25162         this.clearSelections();
25163         var index = this.store.indexOf(record);
25164         var n = this.nodes[index];
25165         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25166         n.parentNode.removeChild(n);
25167         this.updateIndexes(index, index);
25168     },
25169
25170     
25171     
25172 // --------- FIXME     
25173     onAdd : function(ds, records, index)
25174     {
25175         //Roo.log(['on Add', ds, records, index] );        
25176         this.clearSelections();
25177         if(this.nodes.length == 0){
25178             this.refresh();
25179             return;
25180         }
25181         var n = this.nodes[index];
25182         for(var i = 0, len = records.length; i < len; i++){
25183             var d = this.prepareData(records[i].data, i, records[i]);
25184             if(n){
25185                 this.tpl.insertBefore(n, d);
25186             }else{
25187                 
25188                 this.tpl.append(this.el, d);
25189             }
25190         }
25191         this.updateIndexes(index);
25192     },
25193
25194     onRemove : function(ds, record, index){
25195        // Roo.log('onRemove');
25196         this.clearSelections();
25197         var el = this.dataName  ?
25198             this.el.child('.roo-tpl-' + this.dataName) :
25199             this.el; 
25200         
25201         el.dom.removeChild(this.nodes[index]);
25202         this.updateIndexes(index);
25203     },
25204
25205     /**
25206      * Refresh an individual node.
25207      * @param {Number} index
25208      */
25209     refreshNode : function(index){
25210         this.onUpdate(this.store, this.store.getAt(index));
25211     },
25212
25213     updateIndexes : function(startIndex, endIndex){
25214         var ns = this.nodes;
25215         startIndex = startIndex || 0;
25216         endIndex = endIndex || ns.length - 1;
25217         for(var i = startIndex; i <= endIndex; i++){
25218             ns[i].nodeIndex = i;
25219         }
25220     },
25221
25222     /**
25223      * Changes the data store this view uses and refresh the view.
25224      * @param {Store} store
25225      */
25226     setStore : function(store, initial){
25227         if(!initial && this.store){
25228             this.store.un("datachanged", this.refresh);
25229             this.store.un("add", this.onAdd);
25230             this.store.un("remove", this.onRemove);
25231             this.store.un("update", this.onUpdate);
25232             this.store.un("clear", this.refresh);
25233             this.store.un("beforeload", this.onBeforeLoad);
25234             this.store.un("load", this.onLoad);
25235             this.store.un("loadexception", this.onLoad);
25236         }
25237         if(store){
25238           
25239             store.on("datachanged", this.refresh, this);
25240             store.on("add", this.onAdd, this);
25241             store.on("remove", this.onRemove, this);
25242             store.on("update", this.onUpdate, this);
25243             store.on("clear", this.refresh, this);
25244             store.on("beforeload", this.onBeforeLoad, this);
25245             store.on("load", this.onLoad, this);
25246             store.on("loadexception", this.onLoad, this);
25247         }
25248         
25249         if(store){
25250             this.refresh();
25251         }
25252     },
25253     /**
25254      * onbeforeLoad - masks the loading area.
25255      *
25256      */
25257     onBeforeLoad : function(store,opts)
25258     {
25259          //Roo.log('onBeforeLoad');   
25260         if (!opts.add) {
25261             this.el.update("");
25262         }
25263         this.el.mask(this.mask ? this.mask : "Loading" ); 
25264     },
25265     onLoad : function ()
25266     {
25267         this.el.unmask();
25268     },
25269     
25270
25271     /**
25272      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25273      * @param {HTMLElement} node
25274      * @return {HTMLElement} The template node
25275      */
25276     findItemFromChild : function(node){
25277         var el = this.dataName  ?
25278             this.el.child('.roo-tpl-' + this.dataName,true) :
25279             this.el.dom; 
25280         
25281         if(!node || node.parentNode == el){
25282                     return node;
25283             }
25284             var p = node.parentNode;
25285             while(p && p != el){
25286             if(p.parentNode == el){
25287                 return p;
25288             }
25289             p = p.parentNode;
25290         }
25291             return null;
25292     },
25293
25294     /** @ignore */
25295     onClick : function(e){
25296         var item = this.findItemFromChild(e.getTarget());
25297         if(item){
25298             var index = this.indexOf(item);
25299             if(this.onItemClick(item, index, e) !== false){
25300                 this.fireEvent("click", this, index, item, e);
25301             }
25302         }else{
25303             this.clearSelections();
25304         }
25305     },
25306
25307     /** @ignore */
25308     onContextMenu : function(e){
25309         var item = this.findItemFromChild(e.getTarget());
25310         if(item){
25311             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25312         }
25313     },
25314
25315     /** @ignore */
25316     onDblClick : function(e){
25317         var item = this.findItemFromChild(e.getTarget());
25318         if(item){
25319             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25320         }
25321     },
25322
25323     onItemClick : function(item, index, e)
25324     {
25325         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25326             return false;
25327         }
25328         if (this.toggleSelect) {
25329             var m = this.isSelected(item) ? 'unselect' : 'select';
25330             //Roo.log(m);
25331             var _t = this;
25332             _t[m](item, true, false);
25333             return true;
25334         }
25335         if(this.multiSelect || this.singleSelect){
25336             if(this.multiSelect && e.shiftKey && this.lastSelection){
25337                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25338             }else{
25339                 this.select(item, this.multiSelect && e.ctrlKey);
25340                 this.lastSelection = item;
25341             }
25342             
25343             if(!this.tickable){
25344                 e.preventDefault();
25345             }
25346             
25347         }
25348         return true;
25349     },
25350
25351     /**
25352      * Get the number of selected nodes.
25353      * @return {Number}
25354      */
25355     getSelectionCount : function(){
25356         return this.selections.length;
25357     },
25358
25359     /**
25360      * Get the currently selected nodes.
25361      * @return {Array} An array of HTMLElements
25362      */
25363     getSelectedNodes : function(){
25364         return this.selections;
25365     },
25366
25367     /**
25368      * Get the indexes of the selected nodes.
25369      * @return {Array}
25370      */
25371     getSelectedIndexes : function(){
25372         var indexes = [], s = this.selections;
25373         for(var i = 0, len = s.length; i < len; i++){
25374             indexes.push(s[i].nodeIndex);
25375         }
25376         return indexes;
25377     },
25378
25379     /**
25380      * Clear all selections
25381      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25382      */
25383     clearSelections : function(suppressEvent){
25384         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25385             this.cmp.elements = this.selections;
25386             this.cmp.removeClass(this.selectedClass);
25387             this.selections = [];
25388             if(!suppressEvent){
25389                 this.fireEvent("selectionchange", this, this.selections);
25390             }
25391         }
25392     },
25393
25394     /**
25395      * Returns true if the passed node is selected
25396      * @param {HTMLElement/Number} node The node or node index
25397      * @return {Boolean}
25398      */
25399     isSelected : function(node){
25400         var s = this.selections;
25401         if(s.length < 1){
25402             return false;
25403         }
25404         node = this.getNode(node);
25405         return s.indexOf(node) !== -1;
25406     },
25407
25408     /**
25409      * Selects nodes.
25410      * @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
25411      * @param {Boolean} keepExisting (optional) true to keep existing selections
25412      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25413      */
25414     select : function(nodeInfo, keepExisting, suppressEvent){
25415         if(nodeInfo instanceof Array){
25416             if(!keepExisting){
25417                 this.clearSelections(true);
25418             }
25419             for(var i = 0, len = nodeInfo.length; i < len; i++){
25420                 this.select(nodeInfo[i], true, true);
25421             }
25422             return;
25423         } 
25424         var node = this.getNode(nodeInfo);
25425         if(!node || this.isSelected(node)){
25426             return; // already selected.
25427         }
25428         if(!keepExisting){
25429             this.clearSelections(true);
25430         }
25431         
25432         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25433             Roo.fly(node).addClass(this.selectedClass);
25434             this.selections.push(node);
25435             if(!suppressEvent){
25436                 this.fireEvent("selectionchange", this, this.selections);
25437             }
25438         }
25439         
25440         
25441     },
25442       /**
25443      * Unselects nodes.
25444      * @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
25445      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25446      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25447      */
25448     unselect : function(nodeInfo, keepExisting, suppressEvent)
25449     {
25450         if(nodeInfo instanceof Array){
25451             Roo.each(this.selections, function(s) {
25452                 this.unselect(s, nodeInfo);
25453             }, this);
25454             return;
25455         }
25456         var node = this.getNode(nodeInfo);
25457         if(!node || !this.isSelected(node)){
25458             //Roo.log("not selected");
25459             return; // not selected.
25460         }
25461         // fireevent???
25462         var ns = [];
25463         Roo.each(this.selections, function(s) {
25464             if (s == node ) {
25465                 Roo.fly(node).removeClass(this.selectedClass);
25466
25467                 return;
25468             }
25469             ns.push(s);
25470         },this);
25471         
25472         this.selections= ns;
25473         this.fireEvent("selectionchange", this, this.selections);
25474     },
25475
25476     /**
25477      * Gets a template node.
25478      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25479      * @return {HTMLElement} The node or null if it wasn't found
25480      */
25481     getNode : function(nodeInfo){
25482         if(typeof nodeInfo == "string"){
25483             return document.getElementById(nodeInfo);
25484         }else if(typeof nodeInfo == "number"){
25485             return this.nodes[nodeInfo];
25486         }
25487         return nodeInfo;
25488     },
25489
25490     /**
25491      * Gets a range template nodes.
25492      * @param {Number} startIndex
25493      * @param {Number} endIndex
25494      * @return {Array} An array of nodes
25495      */
25496     getNodes : function(start, end){
25497         var ns = this.nodes;
25498         start = start || 0;
25499         end = typeof end == "undefined" ? ns.length - 1 : end;
25500         var nodes = [];
25501         if(start <= end){
25502             for(var i = start; i <= end; i++){
25503                 nodes.push(ns[i]);
25504             }
25505         } else{
25506             for(var i = start; i >= end; i--){
25507                 nodes.push(ns[i]);
25508             }
25509         }
25510         return nodes;
25511     },
25512
25513     /**
25514      * Finds the index of the passed node
25515      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25516      * @return {Number} The index of the node or -1
25517      */
25518     indexOf : function(node){
25519         node = this.getNode(node);
25520         if(typeof node.nodeIndex == "number"){
25521             return node.nodeIndex;
25522         }
25523         var ns = this.nodes;
25524         for(var i = 0, len = ns.length; i < len; i++){
25525             if(ns[i] == node){
25526                 return i;
25527             }
25528         }
25529         return -1;
25530     }
25531 });
25532 /*
25533  * Based on:
25534  * Ext JS Library 1.1.1
25535  * Copyright(c) 2006-2007, Ext JS, LLC.
25536  *
25537  * Originally Released Under LGPL - original licence link has changed is not relivant.
25538  *
25539  * Fork - LGPL
25540  * <script type="text/javascript">
25541  */
25542
25543 /**
25544  * @class Roo.JsonView
25545  * @extends Roo.View
25546  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25547 <pre><code>
25548 var view = new Roo.JsonView({
25549     container: "my-element",
25550     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25551     multiSelect: true, 
25552     jsonRoot: "data" 
25553 });
25554
25555 // listen for node click?
25556 view.on("click", function(vw, index, node, e){
25557     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25558 });
25559
25560 // direct load of JSON data
25561 view.load("foobar.php");
25562
25563 // Example from my blog list
25564 var tpl = new Roo.Template(
25565     '&lt;div class="entry"&gt;' +
25566     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25567     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25568     "&lt;/div&gt;&lt;hr /&gt;"
25569 );
25570
25571 var moreView = new Roo.JsonView({
25572     container :  "entry-list", 
25573     template : tpl,
25574     jsonRoot: "posts"
25575 });
25576 moreView.on("beforerender", this.sortEntries, this);
25577 moreView.load({
25578     url: "/blog/get-posts.php",
25579     params: "allposts=true",
25580     text: "Loading Blog Entries..."
25581 });
25582 </code></pre>
25583
25584 * Note: old code is supported with arguments : (container, template, config)
25585
25586
25587  * @constructor
25588  * Create a new JsonView
25589  * 
25590  * @param {Object} config The config object
25591  * 
25592  */
25593 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25594     
25595     
25596     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25597
25598     var um = this.el.getUpdateManager();
25599     um.setRenderer(this);
25600     um.on("update", this.onLoad, this);
25601     um.on("failure", this.onLoadException, this);
25602
25603     /**
25604      * @event beforerender
25605      * Fires before rendering of the downloaded JSON data.
25606      * @param {Roo.JsonView} this
25607      * @param {Object} data The JSON data loaded
25608      */
25609     /**
25610      * @event load
25611      * Fires when data is loaded.
25612      * @param {Roo.JsonView} this
25613      * @param {Object} data The JSON data loaded
25614      * @param {Object} response The raw Connect response object
25615      */
25616     /**
25617      * @event loadexception
25618      * Fires when loading fails.
25619      * @param {Roo.JsonView} this
25620      * @param {Object} response The raw Connect response object
25621      */
25622     this.addEvents({
25623         'beforerender' : true,
25624         'load' : true,
25625         'loadexception' : true
25626     });
25627 };
25628 Roo.extend(Roo.JsonView, Roo.View, {
25629     /**
25630      * @type {String} The root property in the loaded JSON object that contains the data
25631      */
25632     jsonRoot : "",
25633
25634     /**
25635      * Refreshes the view.
25636      */
25637     refresh : function(){
25638         this.clearSelections();
25639         this.el.update("");
25640         var html = [];
25641         var o = this.jsonData;
25642         if(o && o.length > 0){
25643             for(var i = 0, len = o.length; i < len; i++){
25644                 var data = this.prepareData(o[i], i, o);
25645                 html[html.length] = this.tpl.apply(data);
25646             }
25647         }else{
25648             html.push(this.emptyText);
25649         }
25650         this.el.update(html.join(""));
25651         this.nodes = this.el.dom.childNodes;
25652         this.updateIndexes(0);
25653     },
25654
25655     /**
25656      * 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.
25657      * @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:
25658      <pre><code>
25659      view.load({
25660          url: "your-url.php",
25661          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25662          callback: yourFunction,
25663          scope: yourObject, //(optional scope)
25664          discardUrl: false,
25665          nocache: false,
25666          text: "Loading...",
25667          timeout: 30,
25668          scripts: false
25669      });
25670      </code></pre>
25671      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25672      * 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.
25673      * @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}
25674      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25675      * @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.
25676      */
25677     load : function(){
25678         var um = this.el.getUpdateManager();
25679         um.update.apply(um, arguments);
25680     },
25681
25682     render : function(el, response){
25683         this.clearSelections();
25684         this.el.update("");
25685         var o;
25686         try{
25687             o = Roo.util.JSON.decode(response.responseText);
25688             if(this.jsonRoot){
25689                 
25690                 o = o[this.jsonRoot];
25691             }
25692         } catch(e){
25693         }
25694         /**
25695          * The current JSON data or null
25696          */
25697         this.jsonData = o;
25698         this.beforeRender();
25699         this.refresh();
25700     },
25701
25702 /**
25703  * Get the number of records in the current JSON dataset
25704  * @return {Number}
25705  */
25706     getCount : function(){
25707         return this.jsonData ? this.jsonData.length : 0;
25708     },
25709
25710 /**
25711  * Returns the JSON object for the specified node(s)
25712  * @param {HTMLElement/Array} node The node or an array of nodes
25713  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25714  * you get the JSON object for the node
25715  */
25716     getNodeData : function(node){
25717         if(node instanceof Array){
25718             var data = [];
25719             for(var i = 0, len = node.length; i < len; i++){
25720                 data.push(this.getNodeData(node[i]));
25721             }
25722             return data;
25723         }
25724         return this.jsonData[this.indexOf(node)] || null;
25725     },
25726
25727     beforeRender : function(){
25728         this.snapshot = this.jsonData;
25729         if(this.sortInfo){
25730             this.sort.apply(this, this.sortInfo);
25731         }
25732         this.fireEvent("beforerender", this, this.jsonData);
25733     },
25734
25735     onLoad : function(el, o){
25736         this.fireEvent("load", this, this.jsonData, o);
25737     },
25738
25739     onLoadException : function(el, o){
25740         this.fireEvent("loadexception", this, o);
25741     },
25742
25743 /**
25744  * Filter the data by a specific property.
25745  * @param {String} property A property on your JSON objects
25746  * @param {String/RegExp} value Either string that the property values
25747  * should start with, or a RegExp to test against the property
25748  */
25749     filter : function(property, value){
25750         if(this.jsonData){
25751             var data = [];
25752             var ss = this.snapshot;
25753             if(typeof value == "string"){
25754                 var vlen = value.length;
25755                 if(vlen == 0){
25756                     this.clearFilter();
25757                     return;
25758                 }
25759                 value = value.toLowerCase();
25760                 for(var i = 0, len = ss.length; i < len; i++){
25761                     var o = ss[i];
25762                     if(o[property].substr(0, vlen).toLowerCase() == value){
25763                         data.push(o);
25764                     }
25765                 }
25766             } else if(value.exec){ // regex?
25767                 for(var i = 0, len = ss.length; i < len; i++){
25768                     var o = ss[i];
25769                     if(value.test(o[property])){
25770                         data.push(o);
25771                     }
25772                 }
25773             } else{
25774                 return;
25775             }
25776             this.jsonData = data;
25777             this.refresh();
25778         }
25779     },
25780
25781 /**
25782  * Filter by a function. The passed function will be called with each
25783  * object in the current dataset. If the function returns true the value is kept,
25784  * otherwise it is filtered.
25785  * @param {Function} fn
25786  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25787  */
25788     filterBy : function(fn, scope){
25789         if(this.jsonData){
25790             var data = [];
25791             var ss = this.snapshot;
25792             for(var i = 0, len = ss.length; i < len; i++){
25793                 var o = ss[i];
25794                 if(fn.call(scope || this, o)){
25795                     data.push(o);
25796                 }
25797             }
25798             this.jsonData = data;
25799             this.refresh();
25800         }
25801     },
25802
25803 /**
25804  * Clears the current filter.
25805  */
25806     clearFilter : function(){
25807         if(this.snapshot && this.jsonData != this.snapshot){
25808             this.jsonData = this.snapshot;
25809             this.refresh();
25810         }
25811     },
25812
25813
25814 /**
25815  * Sorts the data for this view and refreshes it.
25816  * @param {String} property A property on your JSON objects to sort on
25817  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25818  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25819  */
25820     sort : function(property, dir, sortType){
25821         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25822         if(this.jsonData){
25823             var p = property;
25824             var dsc = dir && dir.toLowerCase() == "desc";
25825             var f = function(o1, o2){
25826                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25827                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25828                 ;
25829                 if(v1 < v2){
25830                     return dsc ? +1 : -1;
25831                 } else if(v1 > v2){
25832                     return dsc ? -1 : +1;
25833                 } else{
25834                     return 0;
25835                 }
25836             };
25837             this.jsonData.sort(f);
25838             this.refresh();
25839             if(this.jsonData != this.snapshot){
25840                 this.snapshot.sort(f);
25841             }
25842         }
25843     }
25844 });/*
25845  * Based on:
25846  * Ext JS Library 1.1.1
25847  * Copyright(c) 2006-2007, Ext JS, LLC.
25848  *
25849  * Originally Released Under LGPL - original licence link has changed is not relivant.
25850  *
25851  * Fork - LGPL
25852  * <script type="text/javascript">
25853  */
25854  
25855
25856 /**
25857  * @class Roo.ColorPalette
25858  * @extends Roo.Component
25859  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25860  * Here's an example of typical usage:
25861  * <pre><code>
25862 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25863 cp.render('my-div');
25864
25865 cp.on('select', function(palette, selColor){
25866     // do something with selColor
25867 });
25868 </code></pre>
25869  * @constructor
25870  * Create a new ColorPalette
25871  * @param {Object} config The config object
25872  */
25873 Roo.ColorPalette = function(config){
25874     Roo.ColorPalette.superclass.constructor.call(this, config);
25875     this.addEvents({
25876         /**
25877              * @event select
25878              * Fires when a color is selected
25879              * @param {ColorPalette} this
25880              * @param {String} color The 6-digit color hex code (without the # symbol)
25881              */
25882         select: true
25883     });
25884
25885     if(this.handler){
25886         this.on("select", this.handler, this.scope, true);
25887     }
25888 };
25889 Roo.extend(Roo.ColorPalette, Roo.Component, {
25890     /**
25891      * @cfg {String} itemCls
25892      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25893      */
25894     itemCls : "x-color-palette",
25895     /**
25896      * @cfg {String} value
25897      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25898      * the hex codes are case-sensitive.
25899      */
25900     value : null,
25901     clickEvent:'click',
25902     // private
25903     ctype: "Roo.ColorPalette",
25904
25905     /**
25906      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25907      */
25908     allowReselect : false,
25909
25910     /**
25911      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25912      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25913      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25914      * of colors with the width setting until the box is symmetrical.</p>
25915      * <p>You can override individual colors if needed:</p>
25916      * <pre><code>
25917 var cp = new Roo.ColorPalette();
25918 cp.colors[0] = "FF0000";  // change the first box to red
25919 </code></pre>
25920
25921 Or you can provide a custom array of your own for complete control:
25922 <pre><code>
25923 var cp = new Roo.ColorPalette();
25924 cp.colors = ["000000", "993300", "333300"];
25925 </code></pre>
25926      * @type Array
25927      */
25928     colors : [
25929         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25930         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25931         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25932         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25933         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25934     ],
25935
25936     // private
25937     onRender : function(container, position){
25938         var t = new Roo.MasterTemplate(
25939             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25940         );
25941         var c = this.colors;
25942         for(var i = 0, len = c.length; i < len; i++){
25943             t.add([c[i]]);
25944         }
25945         var el = document.createElement("div");
25946         el.className = this.itemCls;
25947         t.overwrite(el);
25948         container.dom.insertBefore(el, position);
25949         this.el = Roo.get(el);
25950         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25951         if(this.clickEvent != 'click'){
25952             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25953         }
25954     },
25955
25956     // private
25957     afterRender : function(){
25958         Roo.ColorPalette.superclass.afterRender.call(this);
25959         if(this.value){
25960             var s = this.value;
25961             this.value = null;
25962             this.select(s);
25963         }
25964     },
25965
25966     // private
25967     handleClick : function(e, t){
25968         e.preventDefault();
25969         if(!this.disabled){
25970             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25971             this.select(c.toUpperCase());
25972         }
25973     },
25974
25975     /**
25976      * Selects the specified color in the palette (fires the select event)
25977      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25978      */
25979     select : function(color){
25980         color = color.replace("#", "");
25981         if(color != this.value || this.allowReselect){
25982             var el = this.el;
25983             if(this.value){
25984                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25985             }
25986             el.child("a.color-"+color).addClass("x-color-palette-sel");
25987             this.value = color;
25988             this.fireEvent("select", this, color);
25989         }
25990     }
25991 });/*
25992  * Based on:
25993  * Ext JS Library 1.1.1
25994  * Copyright(c) 2006-2007, Ext JS, LLC.
25995  *
25996  * Originally Released Under LGPL - original licence link has changed is not relivant.
25997  *
25998  * Fork - LGPL
25999  * <script type="text/javascript">
26000  */
26001  
26002 /**
26003  * @class Roo.DatePicker
26004  * @extends Roo.Component
26005  * Simple date picker class.
26006  * @constructor
26007  * Create a new DatePicker
26008  * @param {Object} config The config object
26009  */
26010 Roo.DatePicker = function(config){
26011     Roo.DatePicker.superclass.constructor.call(this, config);
26012
26013     this.value = config && config.value ?
26014                  config.value.clearTime() : new Date().clearTime();
26015
26016     this.addEvents({
26017         /**
26018              * @event select
26019              * Fires when a date is selected
26020              * @param {DatePicker} this
26021              * @param {Date} date The selected date
26022              */
26023         'select': true,
26024         /**
26025              * @event monthchange
26026              * Fires when the displayed month changes 
26027              * @param {DatePicker} this
26028              * @param {Date} date The selected month
26029              */
26030         'monthchange': true
26031     });
26032
26033     if(this.handler){
26034         this.on("select", this.handler,  this.scope || this);
26035     }
26036     // build the disabledDatesRE
26037     if(!this.disabledDatesRE && this.disabledDates){
26038         var dd = this.disabledDates;
26039         var re = "(?:";
26040         for(var i = 0; i < dd.length; i++){
26041             re += dd[i];
26042             if(i != dd.length-1) re += "|";
26043         }
26044         this.disabledDatesRE = new RegExp(re + ")");
26045     }
26046 };
26047
26048 Roo.extend(Roo.DatePicker, Roo.Component, {
26049     /**
26050      * @cfg {String} todayText
26051      * The text to display on the button that selects the current date (defaults to "Today")
26052      */
26053     todayText : "Today",
26054     /**
26055      * @cfg {String} okText
26056      * The text to display on the ok button
26057      */
26058     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26059     /**
26060      * @cfg {String} cancelText
26061      * The text to display on the cancel button
26062      */
26063     cancelText : "Cancel",
26064     /**
26065      * @cfg {String} todayTip
26066      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26067      */
26068     todayTip : "{0} (Spacebar)",
26069     /**
26070      * @cfg {Date} minDate
26071      * Minimum allowable date (JavaScript date object, defaults to null)
26072      */
26073     minDate : null,
26074     /**
26075      * @cfg {Date} maxDate
26076      * Maximum allowable date (JavaScript date object, defaults to null)
26077      */
26078     maxDate : null,
26079     /**
26080      * @cfg {String} minText
26081      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26082      */
26083     minText : "This date is before the minimum date",
26084     /**
26085      * @cfg {String} maxText
26086      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26087      */
26088     maxText : "This date is after the maximum date",
26089     /**
26090      * @cfg {String} format
26091      * The default date format string which can be overriden for localization support.  The format must be
26092      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26093      */
26094     format : "m/d/y",
26095     /**
26096      * @cfg {Array} disabledDays
26097      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26098      */
26099     disabledDays : null,
26100     /**
26101      * @cfg {String} disabledDaysText
26102      * The tooltip to display when the date falls on a disabled day (defaults to "")
26103      */
26104     disabledDaysText : "",
26105     /**
26106      * @cfg {RegExp} disabledDatesRE
26107      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26108      */
26109     disabledDatesRE : null,
26110     /**
26111      * @cfg {String} disabledDatesText
26112      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26113      */
26114     disabledDatesText : "",
26115     /**
26116      * @cfg {Boolean} constrainToViewport
26117      * True to constrain the date picker to the viewport (defaults to true)
26118      */
26119     constrainToViewport : true,
26120     /**
26121      * @cfg {Array} monthNames
26122      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26123      */
26124     monthNames : Date.monthNames,
26125     /**
26126      * @cfg {Array} dayNames
26127      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26128      */
26129     dayNames : Date.dayNames,
26130     /**
26131      * @cfg {String} nextText
26132      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26133      */
26134     nextText: 'Next Month (Control+Right)',
26135     /**
26136      * @cfg {String} prevText
26137      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26138      */
26139     prevText: 'Previous Month (Control+Left)',
26140     /**
26141      * @cfg {String} monthYearText
26142      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26143      */
26144     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26145     /**
26146      * @cfg {Number} startDay
26147      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26148      */
26149     startDay : 0,
26150     /**
26151      * @cfg {Bool} showClear
26152      * Show a clear button (usefull for date form elements that can be blank.)
26153      */
26154     
26155     showClear: false,
26156     
26157     /**
26158      * Sets the value of the date field
26159      * @param {Date} value The date to set
26160      */
26161     setValue : function(value){
26162         var old = this.value;
26163         
26164         if (typeof(value) == 'string') {
26165          
26166             value = Date.parseDate(value, this.format);
26167         }
26168         if (!value) {
26169             value = new Date();
26170         }
26171         
26172         this.value = value.clearTime(true);
26173         if(this.el){
26174             this.update(this.value);
26175         }
26176     },
26177
26178     /**
26179      * Gets the current selected value of the date field
26180      * @return {Date} The selected date
26181      */
26182     getValue : function(){
26183         return this.value;
26184     },
26185
26186     // private
26187     focus : function(){
26188         if(this.el){
26189             this.update(this.activeDate);
26190         }
26191     },
26192
26193     // privateval
26194     onRender : function(container, position){
26195         
26196         var m = [
26197              '<table cellspacing="0">',
26198                 '<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>',
26199                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26200         var dn = this.dayNames;
26201         for(var i = 0; i < 7; i++){
26202             var d = this.startDay+i;
26203             if(d > 6){
26204                 d = d-7;
26205             }
26206             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26207         }
26208         m[m.length] = "</tr></thead><tbody><tr>";
26209         for(var i = 0; i < 42; i++) {
26210             if(i % 7 == 0 && i != 0){
26211                 m[m.length] = "</tr><tr>";
26212             }
26213             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26214         }
26215         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26216             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26217
26218         var el = document.createElement("div");
26219         el.className = "x-date-picker";
26220         el.innerHTML = m.join("");
26221
26222         container.dom.insertBefore(el, position);
26223
26224         this.el = Roo.get(el);
26225         this.eventEl = Roo.get(el.firstChild);
26226
26227         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26228             handler: this.showPrevMonth,
26229             scope: this,
26230             preventDefault:true,
26231             stopDefault:true
26232         });
26233
26234         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26235             handler: this.showNextMonth,
26236             scope: this,
26237             preventDefault:true,
26238             stopDefault:true
26239         });
26240
26241         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26242
26243         this.monthPicker = this.el.down('div.x-date-mp');
26244         this.monthPicker.enableDisplayMode('block');
26245         
26246         var kn = new Roo.KeyNav(this.eventEl, {
26247             "left" : function(e){
26248                 e.ctrlKey ?
26249                     this.showPrevMonth() :
26250                     this.update(this.activeDate.add("d", -1));
26251             },
26252
26253             "right" : function(e){
26254                 e.ctrlKey ?
26255                     this.showNextMonth() :
26256                     this.update(this.activeDate.add("d", 1));
26257             },
26258
26259             "up" : function(e){
26260                 e.ctrlKey ?
26261                     this.showNextYear() :
26262                     this.update(this.activeDate.add("d", -7));
26263             },
26264
26265             "down" : function(e){
26266                 e.ctrlKey ?
26267                     this.showPrevYear() :
26268                     this.update(this.activeDate.add("d", 7));
26269             },
26270
26271             "pageUp" : function(e){
26272                 this.showNextMonth();
26273             },
26274
26275             "pageDown" : function(e){
26276                 this.showPrevMonth();
26277             },
26278
26279             "enter" : function(e){
26280                 e.stopPropagation();
26281                 return true;
26282             },
26283
26284             scope : this
26285         });
26286
26287         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26288
26289         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26290
26291         this.el.unselectable();
26292         
26293         this.cells = this.el.select("table.x-date-inner tbody td");
26294         this.textNodes = this.el.query("table.x-date-inner tbody span");
26295
26296         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26297             text: "&#160;",
26298             tooltip: this.monthYearText
26299         });
26300
26301         this.mbtn.on('click', this.showMonthPicker, this);
26302         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26303
26304
26305         var today = (new Date()).dateFormat(this.format);
26306         
26307         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26308         if (this.showClear) {
26309             baseTb.add( new Roo.Toolbar.Fill());
26310         }
26311         baseTb.add({
26312             text: String.format(this.todayText, today),
26313             tooltip: String.format(this.todayTip, today),
26314             handler: this.selectToday,
26315             scope: this
26316         });
26317         
26318         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26319             
26320         //});
26321         if (this.showClear) {
26322             
26323             baseTb.add( new Roo.Toolbar.Fill());
26324             baseTb.add({
26325                 text: '&#160;',
26326                 cls: 'x-btn-icon x-btn-clear',
26327                 handler: function() {
26328                     //this.value = '';
26329                     this.fireEvent("select", this, '');
26330                 },
26331                 scope: this
26332             });
26333         }
26334         
26335         
26336         if(Roo.isIE){
26337             this.el.repaint();
26338         }
26339         this.update(this.value);
26340     },
26341
26342     createMonthPicker : function(){
26343         if(!this.monthPicker.dom.firstChild){
26344             var buf = ['<table border="0" cellspacing="0">'];
26345             for(var i = 0; i < 6; i++){
26346                 buf.push(
26347                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26348                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26349                     i == 0 ?
26350                     '<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>' :
26351                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26352                 );
26353             }
26354             buf.push(
26355                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26356                     this.okText,
26357                     '</button><button type="button" class="x-date-mp-cancel">',
26358                     this.cancelText,
26359                     '</button></td></tr>',
26360                 '</table>'
26361             );
26362             this.monthPicker.update(buf.join(''));
26363             this.monthPicker.on('click', this.onMonthClick, this);
26364             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26365
26366             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26367             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26368
26369             this.mpMonths.each(function(m, a, i){
26370                 i += 1;
26371                 if((i%2) == 0){
26372                     m.dom.xmonth = 5 + Math.round(i * .5);
26373                 }else{
26374                     m.dom.xmonth = Math.round((i-1) * .5);
26375                 }
26376             });
26377         }
26378     },
26379
26380     showMonthPicker : function(){
26381         this.createMonthPicker();
26382         var size = this.el.getSize();
26383         this.monthPicker.setSize(size);
26384         this.monthPicker.child('table').setSize(size);
26385
26386         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26387         this.updateMPMonth(this.mpSelMonth);
26388         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26389         this.updateMPYear(this.mpSelYear);
26390
26391         this.monthPicker.slideIn('t', {duration:.2});
26392     },
26393
26394     updateMPYear : function(y){
26395         this.mpyear = y;
26396         var ys = this.mpYears.elements;
26397         for(var i = 1; i <= 10; i++){
26398             var td = ys[i-1], y2;
26399             if((i%2) == 0){
26400                 y2 = y + Math.round(i * .5);
26401                 td.firstChild.innerHTML = y2;
26402                 td.xyear = y2;
26403             }else{
26404                 y2 = y - (5-Math.round(i * .5));
26405                 td.firstChild.innerHTML = y2;
26406                 td.xyear = y2;
26407             }
26408             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26409         }
26410     },
26411
26412     updateMPMonth : function(sm){
26413         this.mpMonths.each(function(m, a, i){
26414             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26415         });
26416     },
26417
26418     selectMPMonth: function(m){
26419         
26420     },
26421
26422     onMonthClick : function(e, t){
26423         e.stopEvent();
26424         var el = new Roo.Element(t), pn;
26425         if(el.is('button.x-date-mp-cancel')){
26426             this.hideMonthPicker();
26427         }
26428         else if(el.is('button.x-date-mp-ok')){
26429             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26430             this.hideMonthPicker();
26431         }
26432         else if(pn = el.up('td.x-date-mp-month', 2)){
26433             this.mpMonths.removeClass('x-date-mp-sel');
26434             pn.addClass('x-date-mp-sel');
26435             this.mpSelMonth = pn.dom.xmonth;
26436         }
26437         else if(pn = el.up('td.x-date-mp-year', 2)){
26438             this.mpYears.removeClass('x-date-mp-sel');
26439             pn.addClass('x-date-mp-sel');
26440             this.mpSelYear = pn.dom.xyear;
26441         }
26442         else if(el.is('a.x-date-mp-prev')){
26443             this.updateMPYear(this.mpyear-10);
26444         }
26445         else if(el.is('a.x-date-mp-next')){
26446             this.updateMPYear(this.mpyear+10);
26447         }
26448     },
26449
26450     onMonthDblClick : function(e, t){
26451         e.stopEvent();
26452         var el = new Roo.Element(t), pn;
26453         if(pn = el.up('td.x-date-mp-month', 2)){
26454             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26455             this.hideMonthPicker();
26456         }
26457         else if(pn = el.up('td.x-date-mp-year', 2)){
26458             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26459             this.hideMonthPicker();
26460         }
26461     },
26462
26463     hideMonthPicker : function(disableAnim){
26464         if(this.monthPicker){
26465             if(disableAnim === true){
26466                 this.monthPicker.hide();
26467             }else{
26468                 this.monthPicker.slideOut('t', {duration:.2});
26469             }
26470         }
26471     },
26472
26473     // private
26474     showPrevMonth : function(e){
26475         this.update(this.activeDate.add("mo", -1));
26476     },
26477
26478     // private
26479     showNextMonth : function(e){
26480         this.update(this.activeDate.add("mo", 1));
26481     },
26482
26483     // private
26484     showPrevYear : function(){
26485         this.update(this.activeDate.add("y", -1));
26486     },
26487
26488     // private
26489     showNextYear : function(){
26490         this.update(this.activeDate.add("y", 1));
26491     },
26492
26493     // private
26494     handleMouseWheel : function(e){
26495         var delta = e.getWheelDelta();
26496         if(delta > 0){
26497             this.showPrevMonth();
26498             e.stopEvent();
26499         } else if(delta < 0){
26500             this.showNextMonth();
26501             e.stopEvent();
26502         }
26503     },
26504
26505     // private
26506     handleDateClick : function(e, t){
26507         e.stopEvent();
26508         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26509             this.setValue(new Date(t.dateValue));
26510             this.fireEvent("select", this, this.value);
26511         }
26512     },
26513
26514     // private
26515     selectToday : function(){
26516         this.setValue(new Date().clearTime());
26517         this.fireEvent("select", this, this.value);
26518     },
26519
26520     // private
26521     update : function(date)
26522     {
26523         var vd = this.activeDate;
26524         this.activeDate = date;
26525         if(vd && this.el){
26526             var t = date.getTime();
26527             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26528                 this.cells.removeClass("x-date-selected");
26529                 this.cells.each(function(c){
26530                    if(c.dom.firstChild.dateValue == t){
26531                        c.addClass("x-date-selected");
26532                        setTimeout(function(){
26533                             try{c.dom.firstChild.focus();}catch(e){}
26534                        }, 50);
26535                        return false;
26536                    }
26537                 });
26538                 return;
26539             }
26540         }
26541         
26542         var days = date.getDaysInMonth();
26543         var firstOfMonth = date.getFirstDateOfMonth();
26544         var startingPos = firstOfMonth.getDay()-this.startDay;
26545
26546         if(startingPos <= this.startDay){
26547             startingPos += 7;
26548         }
26549
26550         var pm = date.add("mo", -1);
26551         var prevStart = pm.getDaysInMonth()-startingPos;
26552
26553         var cells = this.cells.elements;
26554         var textEls = this.textNodes;
26555         days += startingPos;
26556
26557         // convert everything to numbers so it's fast
26558         var day = 86400000;
26559         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26560         var today = new Date().clearTime().getTime();
26561         var sel = date.clearTime().getTime();
26562         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26563         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26564         var ddMatch = this.disabledDatesRE;
26565         var ddText = this.disabledDatesText;
26566         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26567         var ddaysText = this.disabledDaysText;
26568         var format = this.format;
26569
26570         var setCellClass = function(cal, cell){
26571             cell.title = "";
26572             var t = d.getTime();
26573             cell.firstChild.dateValue = t;
26574             if(t == today){
26575                 cell.className += " x-date-today";
26576                 cell.title = cal.todayText;
26577             }
26578             if(t == sel){
26579                 cell.className += " x-date-selected";
26580                 setTimeout(function(){
26581                     try{cell.firstChild.focus();}catch(e){}
26582                 }, 50);
26583             }
26584             // disabling
26585             if(t < min) {
26586                 cell.className = " x-date-disabled";
26587                 cell.title = cal.minText;
26588                 return;
26589             }
26590             if(t > max) {
26591                 cell.className = " x-date-disabled";
26592                 cell.title = cal.maxText;
26593                 return;
26594             }
26595             if(ddays){
26596                 if(ddays.indexOf(d.getDay()) != -1){
26597                     cell.title = ddaysText;
26598                     cell.className = " x-date-disabled";
26599                 }
26600             }
26601             if(ddMatch && format){
26602                 var fvalue = d.dateFormat(format);
26603                 if(ddMatch.test(fvalue)){
26604                     cell.title = ddText.replace("%0", fvalue);
26605                     cell.className = " x-date-disabled";
26606                 }
26607             }
26608         };
26609
26610         var i = 0;
26611         for(; i < startingPos; i++) {
26612             textEls[i].innerHTML = (++prevStart);
26613             d.setDate(d.getDate()+1);
26614             cells[i].className = "x-date-prevday";
26615             setCellClass(this, cells[i]);
26616         }
26617         for(; i < days; i++){
26618             intDay = i - startingPos + 1;
26619             textEls[i].innerHTML = (intDay);
26620             d.setDate(d.getDate()+1);
26621             cells[i].className = "x-date-active";
26622             setCellClass(this, cells[i]);
26623         }
26624         var extraDays = 0;
26625         for(; i < 42; i++) {
26626              textEls[i].innerHTML = (++extraDays);
26627              d.setDate(d.getDate()+1);
26628              cells[i].className = "x-date-nextday";
26629              setCellClass(this, cells[i]);
26630         }
26631
26632         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26633         this.fireEvent('monthchange', this, date);
26634         
26635         if(!this.internalRender){
26636             var main = this.el.dom.firstChild;
26637             var w = main.offsetWidth;
26638             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26639             Roo.fly(main).setWidth(w);
26640             this.internalRender = true;
26641             // opera does not respect the auto grow header center column
26642             // then, after it gets a width opera refuses to recalculate
26643             // without a second pass
26644             if(Roo.isOpera && !this.secondPass){
26645                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26646                 this.secondPass = true;
26647                 this.update.defer(10, this, [date]);
26648             }
26649         }
26650         
26651         
26652     }
26653 });        /*
26654  * Based on:
26655  * Ext JS Library 1.1.1
26656  * Copyright(c) 2006-2007, Ext JS, LLC.
26657  *
26658  * Originally Released Under LGPL - original licence link has changed is not relivant.
26659  *
26660  * Fork - LGPL
26661  * <script type="text/javascript">
26662  */
26663 /**
26664  * @class Roo.TabPanel
26665  * @extends Roo.util.Observable
26666  * A lightweight tab container.
26667  * <br><br>
26668  * Usage:
26669  * <pre><code>
26670 // basic tabs 1, built from existing content
26671 var tabs = new Roo.TabPanel("tabs1");
26672 tabs.addTab("script", "View Script");
26673 tabs.addTab("markup", "View Markup");
26674 tabs.activate("script");
26675
26676 // more advanced tabs, built from javascript
26677 var jtabs = new Roo.TabPanel("jtabs");
26678 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26679
26680 // set up the UpdateManager
26681 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26682 var updater = tab2.getUpdateManager();
26683 updater.setDefaultUrl("ajax1.htm");
26684 tab2.on('activate', updater.refresh, updater, true);
26685
26686 // Use setUrl for Ajax loading
26687 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26688 tab3.setUrl("ajax2.htm", null, true);
26689
26690 // Disabled tab
26691 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26692 tab4.disable();
26693
26694 jtabs.activate("jtabs-1");
26695  * </code></pre>
26696  * @constructor
26697  * Create a new TabPanel.
26698  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26699  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26700  */
26701 Roo.TabPanel = function(container, config){
26702     /**
26703     * The container element for this TabPanel.
26704     * @type Roo.Element
26705     */
26706     this.el = Roo.get(container, true);
26707     if(config){
26708         if(typeof config == "boolean"){
26709             this.tabPosition = config ? "bottom" : "top";
26710         }else{
26711             Roo.apply(this, config);
26712         }
26713     }
26714     if(this.tabPosition == "bottom"){
26715         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26716         this.el.addClass("x-tabs-bottom");
26717     }
26718     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26719     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26720     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26721     if(Roo.isIE){
26722         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26723     }
26724     if(this.tabPosition != "bottom"){
26725         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26726          * @type Roo.Element
26727          */
26728         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26729         this.el.addClass("x-tabs-top");
26730     }
26731     this.items = [];
26732
26733     this.bodyEl.setStyle("position", "relative");
26734
26735     this.active = null;
26736     this.activateDelegate = this.activate.createDelegate(this);
26737
26738     this.addEvents({
26739         /**
26740          * @event tabchange
26741          * Fires when the active tab changes
26742          * @param {Roo.TabPanel} this
26743          * @param {Roo.TabPanelItem} activePanel The new active tab
26744          */
26745         "tabchange": true,
26746         /**
26747          * @event beforetabchange
26748          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26749          * @param {Roo.TabPanel} this
26750          * @param {Object} e Set cancel to true on this object to cancel the tab change
26751          * @param {Roo.TabPanelItem} tab The tab being changed to
26752          */
26753         "beforetabchange" : true
26754     });
26755
26756     Roo.EventManager.onWindowResize(this.onResize, this);
26757     this.cpad = this.el.getPadding("lr");
26758     this.hiddenCount = 0;
26759
26760
26761     // toolbar on the tabbar support...
26762     if (this.toolbar) {
26763         var tcfg = this.toolbar;
26764         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26765         this.toolbar = new Roo.Toolbar(tcfg);
26766         if (Roo.isSafari) {
26767             var tbl = tcfg.container.child('table', true);
26768             tbl.setAttribute('width', '100%');
26769         }
26770         
26771     }
26772    
26773
26774
26775     Roo.TabPanel.superclass.constructor.call(this);
26776 };
26777
26778 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26779     /*
26780      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26781      */
26782     tabPosition : "top",
26783     /*
26784      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26785      */
26786     currentTabWidth : 0,
26787     /*
26788      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26789      */
26790     minTabWidth : 40,
26791     /*
26792      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26793      */
26794     maxTabWidth : 250,
26795     /*
26796      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26797      */
26798     preferredTabWidth : 175,
26799     /*
26800      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26801      */
26802     resizeTabs : false,
26803     /*
26804      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26805      */
26806     monitorResize : true,
26807     /*
26808      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26809      */
26810     toolbar : false,
26811
26812     /**
26813      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26814      * @param {String} id The id of the div to use <b>or create</b>
26815      * @param {String} text The text for the tab
26816      * @param {String} content (optional) Content to put in the TabPanelItem body
26817      * @param {Boolean} closable (optional) True to create a close icon on the tab
26818      * @return {Roo.TabPanelItem} The created TabPanelItem
26819      */
26820     addTab : function(id, text, content, closable){
26821         var item = new Roo.TabPanelItem(this, id, text, closable);
26822         this.addTabItem(item);
26823         if(content){
26824             item.setContent(content);
26825         }
26826         return item;
26827     },
26828
26829     /**
26830      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26831      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26832      * @return {Roo.TabPanelItem}
26833      */
26834     getTab : function(id){
26835         return this.items[id];
26836     },
26837
26838     /**
26839      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26840      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26841      */
26842     hideTab : function(id){
26843         var t = this.items[id];
26844         if(!t.isHidden()){
26845            t.setHidden(true);
26846            this.hiddenCount++;
26847            this.autoSizeTabs();
26848         }
26849     },
26850
26851     /**
26852      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26853      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26854      */
26855     unhideTab : function(id){
26856         var t = this.items[id];
26857         if(t.isHidden()){
26858            t.setHidden(false);
26859            this.hiddenCount--;
26860            this.autoSizeTabs();
26861         }
26862     },
26863
26864     /**
26865      * Adds an existing {@link Roo.TabPanelItem}.
26866      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26867      */
26868     addTabItem : function(item){
26869         this.items[item.id] = item;
26870         this.items.push(item);
26871         if(this.resizeTabs){
26872            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26873            this.autoSizeTabs();
26874         }else{
26875             item.autoSize();
26876         }
26877     },
26878
26879     /**
26880      * Removes a {@link Roo.TabPanelItem}.
26881      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26882      */
26883     removeTab : function(id){
26884         var items = this.items;
26885         var tab = items[id];
26886         if(!tab) { return; }
26887         var index = items.indexOf(tab);
26888         if(this.active == tab && items.length > 1){
26889             var newTab = this.getNextAvailable(index);
26890             if(newTab) {
26891                 newTab.activate();
26892             }
26893         }
26894         this.stripEl.dom.removeChild(tab.pnode.dom);
26895         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26896             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26897         }
26898         items.splice(index, 1);
26899         delete this.items[tab.id];
26900         tab.fireEvent("close", tab);
26901         tab.purgeListeners();
26902         this.autoSizeTabs();
26903     },
26904
26905     getNextAvailable : function(start){
26906         var items = this.items;
26907         var index = start;
26908         // look for a next tab that will slide over to
26909         // replace the one being removed
26910         while(index < items.length){
26911             var item = items[++index];
26912             if(item && !item.isHidden()){
26913                 return item;
26914             }
26915         }
26916         // if one isn't found select the previous tab (on the left)
26917         index = start;
26918         while(index >= 0){
26919             var item = items[--index];
26920             if(item && !item.isHidden()){
26921                 return item;
26922             }
26923         }
26924         return null;
26925     },
26926
26927     /**
26928      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26929      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26930      */
26931     disableTab : function(id){
26932         var tab = this.items[id];
26933         if(tab && this.active != tab){
26934             tab.disable();
26935         }
26936     },
26937
26938     /**
26939      * Enables a {@link Roo.TabPanelItem} that is disabled.
26940      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26941      */
26942     enableTab : function(id){
26943         var tab = this.items[id];
26944         tab.enable();
26945     },
26946
26947     /**
26948      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26949      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26950      * @return {Roo.TabPanelItem} The TabPanelItem.
26951      */
26952     activate : function(id){
26953         var tab = this.items[id];
26954         if(!tab){
26955             return null;
26956         }
26957         if(tab == this.active || tab.disabled){
26958             return tab;
26959         }
26960         var e = {};
26961         this.fireEvent("beforetabchange", this, e, tab);
26962         if(e.cancel !== true && !tab.disabled){
26963             if(this.active){
26964                 this.active.hide();
26965             }
26966             this.active = this.items[id];
26967             this.active.show();
26968             this.fireEvent("tabchange", this, this.active);
26969         }
26970         return tab;
26971     },
26972
26973     /**
26974      * Gets the active {@link Roo.TabPanelItem}.
26975      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26976      */
26977     getActiveTab : function(){
26978         return this.active;
26979     },
26980
26981     /**
26982      * Updates the tab body element to fit the height of the container element
26983      * for overflow scrolling
26984      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26985      */
26986     syncHeight : function(targetHeight){
26987         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26988         var bm = this.bodyEl.getMargins();
26989         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26990         this.bodyEl.setHeight(newHeight);
26991         return newHeight;
26992     },
26993
26994     onResize : function(){
26995         if(this.monitorResize){
26996             this.autoSizeTabs();
26997         }
26998     },
26999
27000     /**
27001      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
27002      */
27003     beginUpdate : function(){
27004         this.updating = true;
27005     },
27006
27007     /**
27008      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27009      */
27010     endUpdate : function(){
27011         this.updating = false;
27012         this.autoSizeTabs();
27013     },
27014
27015     /**
27016      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27017      */
27018     autoSizeTabs : function(){
27019         var count = this.items.length;
27020         var vcount = count - this.hiddenCount;
27021         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27022         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27023         var availWidth = Math.floor(w / vcount);
27024         var b = this.stripBody;
27025         if(b.getWidth() > w){
27026             var tabs = this.items;
27027             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27028             if(availWidth < this.minTabWidth){
27029                 /*if(!this.sleft){    // incomplete scrolling code
27030                     this.createScrollButtons();
27031                 }
27032                 this.showScroll();
27033                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27034             }
27035         }else{
27036             if(this.currentTabWidth < this.preferredTabWidth){
27037                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27038             }
27039         }
27040     },
27041
27042     /**
27043      * Returns the number of tabs in this TabPanel.
27044      * @return {Number}
27045      */
27046      getCount : function(){
27047          return this.items.length;
27048      },
27049
27050     /**
27051      * Resizes all the tabs to the passed width
27052      * @param {Number} The new width
27053      */
27054     setTabWidth : function(width){
27055         this.currentTabWidth = width;
27056         for(var i = 0, len = this.items.length; i < len; i++) {
27057                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27058         }
27059     },
27060
27061     /**
27062      * Destroys this TabPanel
27063      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27064      */
27065     destroy : function(removeEl){
27066         Roo.EventManager.removeResizeListener(this.onResize, this);
27067         for(var i = 0, len = this.items.length; i < len; i++){
27068             this.items[i].purgeListeners();
27069         }
27070         if(removeEl === true){
27071             this.el.update("");
27072             this.el.remove();
27073         }
27074     }
27075 });
27076
27077 /**
27078  * @class Roo.TabPanelItem
27079  * @extends Roo.util.Observable
27080  * Represents an individual item (tab plus body) in a TabPanel.
27081  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27082  * @param {String} id The id of this TabPanelItem
27083  * @param {String} text The text for the tab of this TabPanelItem
27084  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27085  */
27086 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27087     /**
27088      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27089      * @type Roo.TabPanel
27090      */
27091     this.tabPanel = tabPanel;
27092     /**
27093      * The id for this TabPanelItem
27094      * @type String
27095      */
27096     this.id = id;
27097     /** @private */
27098     this.disabled = false;
27099     /** @private */
27100     this.text = text;
27101     /** @private */
27102     this.loaded = false;
27103     this.closable = closable;
27104
27105     /**
27106      * The body element for this TabPanelItem.
27107      * @type Roo.Element
27108      */
27109     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27110     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27111     this.bodyEl.setStyle("display", "block");
27112     this.bodyEl.setStyle("zoom", "1");
27113     this.hideAction();
27114
27115     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27116     /** @private */
27117     this.el = Roo.get(els.el, true);
27118     this.inner = Roo.get(els.inner, true);
27119     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27120     this.pnode = Roo.get(els.el.parentNode, true);
27121     this.el.on("mousedown", this.onTabMouseDown, this);
27122     this.el.on("click", this.onTabClick, this);
27123     /** @private */
27124     if(closable){
27125         var c = Roo.get(els.close, true);
27126         c.dom.title = this.closeText;
27127         c.addClassOnOver("close-over");
27128         c.on("click", this.closeClick, this);
27129      }
27130
27131     this.addEvents({
27132          /**
27133          * @event activate
27134          * Fires when this tab becomes the active tab.
27135          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27136          * @param {Roo.TabPanelItem} this
27137          */
27138         "activate": true,
27139         /**
27140          * @event beforeclose
27141          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27142          * @param {Roo.TabPanelItem} this
27143          * @param {Object} e Set cancel to true on this object to cancel the close.
27144          */
27145         "beforeclose": true,
27146         /**
27147          * @event close
27148          * Fires when this tab is closed.
27149          * @param {Roo.TabPanelItem} this
27150          */
27151          "close": true,
27152         /**
27153          * @event deactivate
27154          * Fires when this tab is no longer the active tab.
27155          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27156          * @param {Roo.TabPanelItem} this
27157          */
27158          "deactivate" : true
27159     });
27160     this.hidden = false;
27161
27162     Roo.TabPanelItem.superclass.constructor.call(this);
27163 };
27164
27165 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27166     purgeListeners : function(){
27167        Roo.util.Observable.prototype.purgeListeners.call(this);
27168        this.el.removeAllListeners();
27169     },
27170     /**
27171      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27172      */
27173     show : function(){
27174         this.pnode.addClass("on");
27175         this.showAction();
27176         if(Roo.isOpera){
27177             this.tabPanel.stripWrap.repaint();
27178         }
27179         this.fireEvent("activate", this.tabPanel, this);
27180     },
27181
27182     /**
27183      * Returns true if this tab is the active tab.
27184      * @return {Boolean}
27185      */
27186     isActive : function(){
27187         return this.tabPanel.getActiveTab() == this;
27188     },
27189
27190     /**
27191      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27192      */
27193     hide : function(){
27194         this.pnode.removeClass("on");
27195         this.hideAction();
27196         this.fireEvent("deactivate", this.tabPanel, this);
27197     },
27198
27199     hideAction : function(){
27200         this.bodyEl.hide();
27201         this.bodyEl.setStyle("position", "absolute");
27202         this.bodyEl.setLeft("-20000px");
27203         this.bodyEl.setTop("-20000px");
27204     },
27205
27206     showAction : function(){
27207         this.bodyEl.setStyle("position", "relative");
27208         this.bodyEl.setTop("");
27209         this.bodyEl.setLeft("");
27210         this.bodyEl.show();
27211     },
27212
27213     /**
27214      * Set the tooltip for the tab.
27215      * @param {String} tooltip The tab's tooltip
27216      */
27217     setTooltip : function(text){
27218         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27219             this.textEl.dom.qtip = text;
27220             this.textEl.dom.removeAttribute('title');
27221         }else{
27222             this.textEl.dom.title = text;
27223         }
27224     },
27225
27226     onTabClick : function(e){
27227         e.preventDefault();
27228         this.tabPanel.activate(this.id);
27229     },
27230
27231     onTabMouseDown : function(e){
27232         e.preventDefault();
27233         this.tabPanel.activate(this.id);
27234     },
27235
27236     getWidth : function(){
27237         return this.inner.getWidth();
27238     },
27239
27240     setWidth : function(width){
27241         var iwidth = width - this.pnode.getPadding("lr");
27242         this.inner.setWidth(iwidth);
27243         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27244         this.pnode.setWidth(width);
27245     },
27246
27247     /**
27248      * Show or hide the tab
27249      * @param {Boolean} hidden True to hide or false to show.
27250      */
27251     setHidden : function(hidden){
27252         this.hidden = hidden;
27253         this.pnode.setStyle("display", hidden ? "none" : "");
27254     },
27255
27256     /**
27257      * Returns true if this tab is "hidden"
27258      * @return {Boolean}
27259      */
27260     isHidden : function(){
27261         return this.hidden;
27262     },
27263
27264     /**
27265      * Returns the text for this tab
27266      * @return {String}
27267      */
27268     getText : function(){
27269         return this.text;
27270     },
27271
27272     autoSize : function(){
27273         //this.el.beginMeasure();
27274         this.textEl.setWidth(1);
27275         /*
27276          *  #2804 [new] Tabs in Roojs
27277          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27278          */
27279         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27280         //this.el.endMeasure();
27281     },
27282
27283     /**
27284      * Sets the text for the tab (Note: this also sets the tooltip text)
27285      * @param {String} text The tab's text and tooltip
27286      */
27287     setText : function(text){
27288         this.text = text;
27289         this.textEl.update(text);
27290         this.setTooltip(text);
27291         if(!this.tabPanel.resizeTabs){
27292             this.autoSize();
27293         }
27294     },
27295     /**
27296      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27297      */
27298     activate : function(){
27299         this.tabPanel.activate(this.id);
27300     },
27301
27302     /**
27303      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27304      */
27305     disable : function(){
27306         if(this.tabPanel.active != this){
27307             this.disabled = true;
27308             this.pnode.addClass("disabled");
27309         }
27310     },
27311
27312     /**
27313      * Enables this TabPanelItem if it was previously disabled.
27314      */
27315     enable : function(){
27316         this.disabled = false;
27317         this.pnode.removeClass("disabled");
27318     },
27319
27320     /**
27321      * Sets the content for this TabPanelItem.
27322      * @param {String} content The content
27323      * @param {Boolean} loadScripts true to look for and load scripts
27324      */
27325     setContent : function(content, loadScripts){
27326         this.bodyEl.update(content, loadScripts);
27327     },
27328
27329     /**
27330      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27331      * @return {Roo.UpdateManager} The UpdateManager
27332      */
27333     getUpdateManager : function(){
27334         return this.bodyEl.getUpdateManager();
27335     },
27336
27337     /**
27338      * Set a URL to be used to load the content for this TabPanelItem.
27339      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27340      * @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)
27341      * @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)
27342      * @return {Roo.UpdateManager} The UpdateManager
27343      */
27344     setUrl : function(url, params, loadOnce){
27345         if(this.refreshDelegate){
27346             this.un('activate', this.refreshDelegate);
27347         }
27348         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27349         this.on("activate", this.refreshDelegate);
27350         return this.bodyEl.getUpdateManager();
27351     },
27352
27353     /** @private */
27354     _handleRefresh : function(url, params, loadOnce){
27355         if(!loadOnce || !this.loaded){
27356             var updater = this.bodyEl.getUpdateManager();
27357             updater.update(url, params, this._setLoaded.createDelegate(this));
27358         }
27359     },
27360
27361     /**
27362      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27363      *   Will fail silently if the setUrl method has not been called.
27364      *   This does not activate the panel, just updates its content.
27365      */
27366     refresh : function(){
27367         if(this.refreshDelegate){
27368            this.loaded = false;
27369            this.refreshDelegate();
27370         }
27371     },
27372
27373     /** @private */
27374     _setLoaded : function(){
27375         this.loaded = true;
27376     },
27377
27378     /** @private */
27379     closeClick : function(e){
27380         var o = {};
27381         e.stopEvent();
27382         this.fireEvent("beforeclose", this, o);
27383         if(o.cancel !== true){
27384             this.tabPanel.removeTab(this.id);
27385         }
27386     },
27387     /**
27388      * The text displayed in the tooltip for the close icon.
27389      * @type String
27390      */
27391     closeText : "Close this tab"
27392 });
27393
27394 /** @private */
27395 Roo.TabPanel.prototype.createStrip = function(container){
27396     var strip = document.createElement("div");
27397     strip.className = "x-tabs-wrap";
27398     container.appendChild(strip);
27399     return strip;
27400 };
27401 /** @private */
27402 Roo.TabPanel.prototype.createStripList = function(strip){
27403     // div wrapper for retard IE
27404     // returns the "tr" element.
27405     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27406         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27407         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27408     return strip.firstChild.firstChild.firstChild.firstChild;
27409 };
27410 /** @private */
27411 Roo.TabPanel.prototype.createBody = function(container){
27412     var body = document.createElement("div");
27413     Roo.id(body, "tab-body");
27414     Roo.fly(body).addClass("x-tabs-body");
27415     container.appendChild(body);
27416     return body;
27417 };
27418 /** @private */
27419 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27420     var body = Roo.getDom(id);
27421     if(!body){
27422         body = document.createElement("div");
27423         body.id = id;
27424     }
27425     Roo.fly(body).addClass("x-tabs-item-body");
27426     bodyEl.insertBefore(body, bodyEl.firstChild);
27427     return body;
27428 };
27429 /** @private */
27430 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27431     var td = document.createElement("td");
27432     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27433     //stripEl.appendChild(td);
27434     if(closable){
27435         td.className = "x-tabs-closable";
27436         if(!this.closeTpl){
27437             this.closeTpl = new Roo.Template(
27438                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27439                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27440                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27441             );
27442         }
27443         var el = this.closeTpl.overwrite(td, {"text": text});
27444         var close = el.getElementsByTagName("div")[0];
27445         var inner = el.getElementsByTagName("em")[0];
27446         return {"el": el, "close": close, "inner": inner};
27447     } else {
27448         if(!this.tabTpl){
27449             this.tabTpl = new Roo.Template(
27450                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27451                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27452             );
27453         }
27454         var el = this.tabTpl.overwrite(td, {"text": text});
27455         var inner = el.getElementsByTagName("em")[0];
27456         return {"el": el, "inner": inner};
27457     }
27458 };/*
27459  * Based on:
27460  * Ext JS Library 1.1.1
27461  * Copyright(c) 2006-2007, Ext JS, LLC.
27462  *
27463  * Originally Released Under LGPL - original licence link has changed is not relivant.
27464  *
27465  * Fork - LGPL
27466  * <script type="text/javascript">
27467  */
27468
27469 /**
27470  * @class Roo.Button
27471  * @extends Roo.util.Observable
27472  * Simple Button class
27473  * @cfg {String} text The button text
27474  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27475  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27476  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27477  * @cfg {Object} scope The scope of the handler
27478  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27479  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27480  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27481  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27482  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27483  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27484    applies if enableToggle = true)
27485  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27486  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27487   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27488  * @constructor
27489  * Create a new button
27490  * @param {Object} config The config object
27491  */
27492 Roo.Button = function(renderTo, config)
27493 {
27494     if (!config) {
27495         config = renderTo;
27496         renderTo = config.renderTo || false;
27497     }
27498     
27499     Roo.apply(this, config);
27500     this.addEvents({
27501         /**
27502              * @event click
27503              * Fires when this button is clicked
27504              * @param {Button} this
27505              * @param {EventObject} e The click event
27506              */
27507             "click" : true,
27508         /**
27509              * @event toggle
27510              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27511              * @param {Button} this
27512              * @param {Boolean} pressed
27513              */
27514             "toggle" : true,
27515         /**
27516              * @event mouseover
27517              * Fires when the mouse hovers over the button
27518              * @param {Button} this
27519              * @param {Event} e The event object
27520              */
27521         'mouseover' : true,
27522         /**
27523              * @event mouseout
27524              * Fires when the mouse exits the button
27525              * @param {Button} this
27526              * @param {Event} e The event object
27527              */
27528         'mouseout': true,
27529          /**
27530              * @event render
27531              * Fires when the button is rendered
27532              * @param {Button} this
27533              */
27534         'render': true
27535     });
27536     if(this.menu){
27537         this.menu = Roo.menu.MenuMgr.get(this.menu);
27538     }
27539     // register listeners first!!  - so render can be captured..
27540     Roo.util.Observable.call(this);
27541     if(renderTo){
27542         this.render(renderTo);
27543     }
27544     
27545   
27546 };
27547
27548 Roo.extend(Roo.Button, Roo.util.Observable, {
27549     /**
27550      * 
27551      */
27552     
27553     /**
27554      * Read-only. True if this button is hidden
27555      * @type Boolean
27556      */
27557     hidden : false,
27558     /**
27559      * Read-only. True if this button is disabled
27560      * @type Boolean
27561      */
27562     disabled : false,
27563     /**
27564      * Read-only. True if this button is pressed (only if enableToggle = true)
27565      * @type Boolean
27566      */
27567     pressed : false,
27568
27569     /**
27570      * @cfg {Number} tabIndex 
27571      * The DOM tabIndex for this button (defaults to undefined)
27572      */
27573     tabIndex : undefined,
27574
27575     /**
27576      * @cfg {Boolean} enableToggle
27577      * True to enable pressed/not pressed toggling (defaults to false)
27578      */
27579     enableToggle: false,
27580     /**
27581      * @cfg {Mixed} menu
27582      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27583      */
27584     menu : undefined,
27585     /**
27586      * @cfg {String} menuAlign
27587      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27588      */
27589     menuAlign : "tl-bl?",
27590
27591     /**
27592      * @cfg {String} iconCls
27593      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27594      */
27595     iconCls : undefined,
27596     /**
27597      * @cfg {String} type
27598      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27599      */
27600     type : 'button',
27601
27602     // private
27603     menuClassTarget: 'tr',
27604
27605     /**
27606      * @cfg {String} clickEvent
27607      * The type of event to map to the button's event handler (defaults to 'click')
27608      */
27609     clickEvent : 'click',
27610
27611     /**
27612      * @cfg {Boolean} handleMouseEvents
27613      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27614      */
27615     handleMouseEvents : true,
27616
27617     /**
27618      * @cfg {String} tooltipType
27619      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27620      */
27621     tooltipType : 'qtip',
27622
27623     /**
27624      * @cfg {String} cls
27625      * A CSS class to apply to the button's main element.
27626      */
27627     
27628     /**
27629      * @cfg {Roo.Template} template (Optional)
27630      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27631      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27632      * require code modifications if required elements (e.g. a button) aren't present.
27633      */
27634
27635     // private
27636     render : function(renderTo){
27637         var btn;
27638         if(this.hideParent){
27639             this.parentEl = Roo.get(renderTo);
27640         }
27641         if(!this.dhconfig){
27642             if(!this.template){
27643                 if(!Roo.Button.buttonTemplate){
27644                     // hideous table template
27645                     Roo.Button.buttonTemplate = new Roo.Template(
27646                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27647                         '<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>',
27648                         "</tr></tbody></table>");
27649                 }
27650                 this.template = Roo.Button.buttonTemplate;
27651             }
27652             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27653             var btnEl = btn.child("button:first");
27654             btnEl.on('focus', this.onFocus, this);
27655             btnEl.on('blur', this.onBlur, this);
27656             if(this.cls){
27657                 btn.addClass(this.cls);
27658             }
27659             if(this.icon){
27660                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27661             }
27662             if(this.iconCls){
27663                 btnEl.addClass(this.iconCls);
27664                 if(!this.cls){
27665                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27666                 }
27667             }
27668             if(this.tabIndex !== undefined){
27669                 btnEl.dom.tabIndex = this.tabIndex;
27670             }
27671             if(this.tooltip){
27672                 if(typeof this.tooltip == 'object'){
27673                     Roo.QuickTips.tips(Roo.apply({
27674                           target: btnEl.id
27675                     }, this.tooltip));
27676                 } else {
27677                     btnEl.dom[this.tooltipType] = this.tooltip;
27678                 }
27679             }
27680         }else{
27681             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27682         }
27683         this.el = btn;
27684         if(this.id){
27685             this.el.dom.id = this.el.id = this.id;
27686         }
27687         if(this.menu){
27688             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27689             this.menu.on("show", this.onMenuShow, this);
27690             this.menu.on("hide", this.onMenuHide, this);
27691         }
27692         btn.addClass("x-btn");
27693         if(Roo.isIE && !Roo.isIE7){
27694             this.autoWidth.defer(1, this);
27695         }else{
27696             this.autoWidth();
27697         }
27698         if(this.handleMouseEvents){
27699             btn.on("mouseover", this.onMouseOver, this);
27700             btn.on("mouseout", this.onMouseOut, this);
27701             btn.on("mousedown", this.onMouseDown, this);
27702         }
27703         btn.on(this.clickEvent, this.onClick, this);
27704         //btn.on("mouseup", this.onMouseUp, this);
27705         if(this.hidden){
27706             this.hide();
27707         }
27708         if(this.disabled){
27709             this.disable();
27710         }
27711         Roo.ButtonToggleMgr.register(this);
27712         if(this.pressed){
27713             this.el.addClass("x-btn-pressed");
27714         }
27715         if(this.repeat){
27716             var repeater = new Roo.util.ClickRepeater(btn,
27717                 typeof this.repeat == "object" ? this.repeat : {}
27718             );
27719             repeater.on("click", this.onClick,  this);
27720         }
27721         
27722         this.fireEvent('render', this);
27723         
27724     },
27725     /**
27726      * Returns the button's underlying element
27727      * @return {Roo.Element} The element
27728      */
27729     getEl : function(){
27730         return this.el;  
27731     },
27732     
27733     /**
27734      * Destroys this Button and removes any listeners.
27735      */
27736     destroy : function(){
27737         Roo.ButtonToggleMgr.unregister(this);
27738         this.el.removeAllListeners();
27739         this.purgeListeners();
27740         this.el.remove();
27741     },
27742
27743     // private
27744     autoWidth : function(){
27745         if(this.el){
27746             this.el.setWidth("auto");
27747             if(Roo.isIE7 && Roo.isStrict){
27748                 var ib = this.el.child('button');
27749                 if(ib && ib.getWidth() > 20){
27750                     ib.clip();
27751                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27752                 }
27753             }
27754             if(this.minWidth){
27755                 if(this.hidden){
27756                     this.el.beginMeasure();
27757                 }
27758                 if(this.el.getWidth() < this.minWidth){
27759                     this.el.setWidth(this.minWidth);
27760                 }
27761                 if(this.hidden){
27762                     this.el.endMeasure();
27763                 }
27764             }
27765         }
27766     },
27767
27768     /**
27769      * Assigns this button's click handler
27770      * @param {Function} handler The function to call when the button is clicked
27771      * @param {Object} scope (optional) Scope for the function passed in
27772      */
27773     setHandler : function(handler, scope){
27774         this.handler = handler;
27775         this.scope = scope;  
27776     },
27777     
27778     /**
27779      * Sets this button's text
27780      * @param {String} text The button text
27781      */
27782     setText : function(text){
27783         this.text = text;
27784         if(this.el){
27785             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27786         }
27787         this.autoWidth();
27788     },
27789     
27790     /**
27791      * Gets the text for this button
27792      * @return {String} The button text
27793      */
27794     getText : function(){
27795         return this.text;  
27796     },
27797     
27798     /**
27799      * Show this button
27800      */
27801     show: function(){
27802         this.hidden = false;
27803         if(this.el){
27804             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27805         }
27806     },
27807     
27808     /**
27809      * Hide this button
27810      */
27811     hide: function(){
27812         this.hidden = true;
27813         if(this.el){
27814             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27815         }
27816     },
27817     
27818     /**
27819      * Convenience function for boolean show/hide
27820      * @param {Boolean} visible True to show, false to hide
27821      */
27822     setVisible: function(visible){
27823         if(visible) {
27824             this.show();
27825         }else{
27826             this.hide();
27827         }
27828     },
27829     
27830     /**
27831      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27832      * @param {Boolean} state (optional) Force a particular state
27833      */
27834     toggle : function(state){
27835         state = state === undefined ? !this.pressed : state;
27836         if(state != this.pressed){
27837             if(state){
27838                 this.el.addClass("x-btn-pressed");
27839                 this.pressed = true;
27840                 this.fireEvent("toggle", this, true);
27841             }else{
27842                 this.el.removeClass("x-btn-pressed");
27843                 this.pressed = false;
27844                 this.fireEvent("toggle", this, false);
27845             }
27846             if(this.toggleHandler){
27847                 this.toggleHandler.call(this.scope || this, this, state);
27848             }
27849         }
27850     },
27851     
27852     /**
27853      * Focus the button
27854      */
27855     focus : function(){
27856         this.el.child('button:first').focus();
27857     },
27858     
27859     /**
27860      * Disable this button
27861      */
27862     disable : function(){
27863         if(this.el){
27864             this.el.addClass("x-btn-disabled");
27865         }
27866         this.disabled = true;
27867     },
27868     
27869     /**
27870      * Enable this button
27871      */
27872     enable : function(){
27873         if(this.el){
27874             this.el.removeClass("x-btn-disabled");
27875         }
27876         this.disabled = false;
27877     },
27878
27879     /**
27880      * Convenience function for boolean enable/disable
27881      * @param {Boolean} enabled True to enable, false to disable
27882      */
27883     setDisabled : function(v){
27884         this[v !== true ? "enable" : "disable"]();
27885     },
27886
27887     // private
27888     onClick : function(e)
27889     {
27890         if(e){
27891             e.preventDefault();
27892         }
27893         if(e.button != 0){
27894             return;
27895         }
27896         if(!this.disabled){
27897             if(this.enableToggle){
27898                 this.toggle();
27899             }
27900             if(this.menu && !this.menu.isVisible()){
27901                 this.menu.show(this.el, this.menuAlign);
27902             }
27903             this.fireEvent("click", this, e);
27904             if(this.handler){
27905                 this.el.removeClass("x-btn-over");
27906                 this.handler.call(this.scope || this, this, e);
27907             }
27908         }
27909     },
27910     // private
27911     onMouseOver : function(e){
27912         if(!this.disabled){
27913             this.el.addClass("x-btn-over");
27914             this.fireEvent('mouseover', this, e);
27915         }
27916     },
27917     // private
27918     onMouseOut : function(e){
27919         if(!e.within(this.el,  true)){
27920             this.el.removeClass("x-btn-over");
27921             this.fireEvent('mouseout', this, e);
27922         }
27923     },
27924     // private
27925     onFocus : function(e){
27926         if(!this.disabled){
27927             this.el.addClass("x-btn-focus");
27928         }
27929     },
27930     // private
27931     onBlur : function(e){
27932         this.el.removeClass("x-btn-focus");
27933     },
27934     // private
27935     onMouseDown : function(e){
27936         if(!this.disabled && e.button == 0){
27937             this.el.addClass("x-btn-click");
27938             Roo.get(document).on('mouseup', this.onMouseUp, this);
27939         }
27940     },
27941     // private
27942     onMouseUp : function(e){
27943         if(e.button == 0){
27944             this.el.removeClass("x-btn-click");
27945             Roo.get(document).un('mouseup', this.onMouseUp, this);
27946         }
27947     },
27948     // private
27949     onMenuShow : function(e){
27950         this.el.addClass("x-btn-menu-active");
27951     },
27952     // private
27953     onMenuHide : function(e){
27954         this.el.removeClass("x-btn-menu-active");
27955     }   
27956 });
27957
27958 // Private utility class used by Button
27959 Roo.ButtonToggleMgr = function(){
27960    var groups = {};
27961    
27962    function toggleGroup(btn, state){
27963        if(state){
27964            var g = groups[btn.toggleGroup];
27965            for(var i = 0, l = g.length; i < l; i++){
27966                if(g[i] != btn){
27967                    g[i].toggle(false);
27968                }
27969            }
27970        }
27971    }
27972    
27973    return {
27974        register : function(btn){
27975            if(!btn.toggleGroup){
27976                return;
27977            }
27978            var g = groups[btn.toggleGroup];
27979            if(!g){
27980                g = groups[btn.toggleGroup] = [];
27981            }
27982            g.push(btn);
27983            btn.on("toggle", toggleGroup);
27984        },
27985        
27986        unregister : function(btn){
27987            if(!btn.toggleGroup){
27988                return;
27989            }
27990            var g = groups[btn.toggleGroup];
27991            if(g){
27992                g.remove(btn);
27993                btn.un("toggle", toggleGroup);
27994            }
27995        }
27996    };
27997 }();/*
27998  * Based on:
27999  * Ext JS Library 1.1.1
28000  * Copyright(c) 2006-2007, Ext JS, LLC.
28001  *
28002  * Originally Released Under LGPL - original licence link has changed is not relivant.
28003  *
28004  * Fork - LGPL
28005  * <script type="text/javascript">
28006  */
28007  
28008 /**
28009  * @class Roo.SplitButton
28010  * @extends Roo.Button
28011  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28012  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28013  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28014  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28015  * @cfg {String} arrowTooltip The title attribute of the arrow
28016  * @constructor
28017  * Create a new menu button
28018  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28019  * @param {Object} config The config object
28020  */
28021 Roo.SplitButton = function(renderTo, config){
28022     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28023     /**
28024      * @event arrowclick
28025      * Fires when this button's arrow is clicked
28026      * @param {SplitButton} this
28027      * @param {EventObject} e The click event
28028      */
28029     this.addEvents({"arrowclick":true});
28030 };
28031
28032 Roo.extend(Roo.SplitButton, Roo.Button, {
28033     render : function(renderTo){
28034         // this is one sweet looking template!
28035         var tpl = new Roo.Template(
28036             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28037             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28038             '<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>',
28039             "</tbody></table></td><td>",
28040             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28041             '<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>',
28042             "</tbody></table></td></tr></table>"
28043         );
28044         var btn = tpl.append(renderTo, [this.text, this.type], true);
28045         var btnEl = btn.child("button");
28046         if(this.cls){
28047             btn.addClass(this.cls);
28048         }
28049         if(this.icon){
28050             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28051         }
28052         if(this.iconCls){
28053             btnEl.addClass(this.iconCls);
28054             if(!this.cls){
28055                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28056             }
28057         }
28058         this.el = btn;
28059         if(this.handleMouseEvents){
28060             btn.on("mouseover", this.onMouseOver, this);
28061             btn.on("mouseout", this.onMouseOut, this);
28062             btn.on("mousedown", this.onMouseDown, this);
28063             btn.on("mouseup", this.onMouseUp, this);
28064         }
28065         btn.on(this.clickEvent, this.onClick, this);
28066         if(this.tooltip){
28067             if(typeof this.tooltip == 'object'){
28068                 Roo.QuickTips.tips(Roo.apply({
28069                       target: btnEl.id
28070                 }, this.tooltip));
28071             } else {
28072                 btnEl.dom[this.tooltipType] = this.tooltip;
28073             }
28074         }
28075         if(this.arrowTooltip){
28076             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28077         }
28078         if(this.hidden){
28079             this.hide();
28080         }
28081         if(this.disabled){
28082             this.disable();
28083         }
28084         if(this.pressed){
28085             this.el.addClass("x-btn-pressed");
28086         }
28087         if(Roo.isIE && !Roo.isIE7){
28088             this.autoWidth.defer(1, this);
28089         }else{
28090             this.autoWidth();
28091         }
28092         if(this.menu){
28093             this.menu.on("show", this.onMenuShow, this);
28094             this.menu.on("hide", this.onMenuHide, this);
28095         }
28096         this.fireEvent('render', this);
28097     },
28098
28099     // private
28100     autoWidth : function(){
28101         if(this.el){
28102             var tbl = this.el.child("table:first");
28103             var tbl2 = this.el.child("table:last");
28104             this.el.setWidth("auto");
28105             tbl.setWidth("auto");
28106             if(Roo.isIE7 && Roo.isStrict){
28107                 var ib = this.el.child('button:first');
28108                 if(ib && ib.getWidth() > 20){
28109                     ib.clip();
28110                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28111                 }
28112             }
28113             if(this.minWidth){
28114                 if(this.hidden){
28115                     this.el.beginMeasure();
28116                 }
28117                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28118                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28119                 }
28120                 if(this.hidden){
28121                     this.el.endMeasure();
28122                 }
28123             }
28124             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28125         } 
28126     },
28127     /**
28128      * Sets this button's click handler
28129      * @param {Function} handler The function to call when the button is clicked
28130      * @param {Object} scope (optional) Scope for the function passed above
28131      */
28132     setHandler : function(handler, scope){
28133         this.handler = handler;
28134         this.scope = scope;  
28135     },
28136     
28137     /**
28138      * Sets this button's arrow click handler
28139      * @param {Function} handler The function to call when the arrow is clicked
28140      * @param {Object} scope (optional) Scope for the function passed above
28141      */
28142     setArrowHandler : function(handler, scope){
28143         this.arrowHandler = handler;
28144         this.scope = scope;  
28145     },
28146     
28147     /**
28148      * Focus the button
28149      */
28150     focus : function(){
28151         if(this.el){
28152             this.el.child("button:first").focus();
28153         }
28154     },
28155
28156     // private
28157     onClick : function(e){
28158         e.preventDefault();
28159         if(!this.disabled){
28160             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28161                 if(this.menu && !this.menu.isVisible()){
28162                     this.menu.show(this.el, this.menuAlign);
28163                 }
28164                 this.fireEvent("arrowclick", this, e);
28165                 if(this.arrowHandler){
28166                     this.arrowHandler.call(this.scope || this, this, e);
28167                 }
28168             }else{
28169                 this.fireEvent("click", this, e);
28170                 if(this.handler){
28171                     this.handler.call(this.scope || this, this, e);
28172                 }
28173             }
28174         }
28175     },
28176     // private
28177     onMouseDown : function(e){
28178         if(!this.disabled){
28179             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28180         }
28181     },
28182     // private
28183     onMouseUp : function(e){
28184         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28185     }   
28186 });
28187
28188
28189 // backwards compat
28190 Roo.MenuButton = Roo.SplitButton;/*
28191  * Based on:
28192  * Ext JS Library 1.1.1
28193  * Copyright(c) 2006-2007, Ext JS, LLC.
28194  *
28195  * Originally Released Under LGPL - original licence link has changed is not relivant.
28196  *
28197  * Fork - LGPL
28198  * <script type="text/javascript">
28199  */
28200
28201 /**
28202  * @class Roo.Toolbar
28203  * Basic Toolbar class.
28204  * @constructor
28205  * Creates a new Toolbar
28206  * @param {Object} container The config object
28207  */ 
28208 Roo.Toolbar = function(container, buttons, config)
28209 {
28210     /// old consturctor format still supported..
28211     if(container instanceof Array){ // omit the container for later rendering
28212         buttons = container;
28213         config = buttons;
28214         container = null;
28215     }
28216     if (typeof(container) == 'object' && container.xtype) {
28217         config = container;
28218         container = config.container;
28219         buttons = config.buttons || []; // not really - use items!!
28220     }
28221     var xitems = [];
28222     if (config && config.items) {
28223         xitems = config.items;
28224         delete config.items;
28225     }
28226     Roo.apply(this, config);
28227     this.buttons = buttons;
28228     
28229     if(container){
28230         this.render(container);
28231     }
28232     this.xitems = xitems;
28233     Roo.each(xitems, function(b) {
28234         this.add(b);
28235     }, this);
28236     
28237 };
28238
28239 Roo.Toolbar.prototype = {
28240     /**
28241      * @cfg {Array} items
28242      * array of button configs or elements to add (will be converted to a MixedCollection)
28243      */
28244     
28245     /**
28246      * @cfg {String/HTMLElement/Element} container
28247      * The id or element that will contain the toolbar
28248      */
28249     // private
28250     render : function(ct){
28251         this.el = Roo.get(ct);
28252         if(this.cls){
28253             this.el.addClass(this.cls);
28254         }
28255         // using a table allows for vertical alignment
28256         // 100% width is needed by Safari...
28257         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28258         this.tr = this.el.child("tr", true);
28259         var autoId = 0;
28260         this.items = new Roo.util.MixedCollection(false, function(o){
28261             return o.id || ("item" + (++autoId));
28262         });
28263         if(this.buttons){
28264             this.add.apply(this, this.buttons);
28265             delete this.buttons;
28266         }
28267     },
28268
28269     /**
28270      * Adds element(s) to the toolbar -- this function takes a variable number of 
28271      * arguments of mixed type and adds them to the toolbar.
28272      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28273      * <ul>
28274      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28275      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28276      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28277      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28278      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28279      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28280      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28281      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28282      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28283      * </ul>
28284      * @param {Mixed} arg2
28285      * @param {Mixed} etc.
28286      */
28287     add : function(){
28288         var a = arguments, l = a.length;
28289         for(var i = 0; i < l; i++){
28290             this._add(a[i]);
28291         }
28292     },
28293     // private..
28294     _add : function(el) {
28295         
28296         if (el.xtype) {
28297             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28298         }
28299         
28300         if (el.applyTo){ // some kind of form field
28301             return this.addField(el);
28302         } 
28303         if (el.render){ // some kind of Toolbar.Item
28304             return this.addItem(el);
28305         }
28306         if (typeof el == "string"){ // string
28307             if(el == "separator" || el == "-"){
28308                 return this.addSeparator();
28309             }
28310             if (el == " "){
28311                 return this.addSpacer();
28312             }
28313             if(el == "->"){
28314                 return this.addFill();
28315             }
28316             return this.addText(el);
28317             
28318         }
28319         if(el.tagName){ // element
28320             return this.addElement(el);
28321         }
28322         if(typeof el == "object"){ // must be button config?
28323             return this.addButton(el);
28324         }
28325         // and now what?!?!
28326         return false;
28327         
28328     },
28329     
28330     /**
28331      * Add an Xtype element
28332      * @param {Object} xtype Xtype Object
28333      * @return {Object} created Object
28334      */
28335     addxtype : function(e){
28336         return this.add(e);  
28337     },
28338     
28339     /**
28340      * Returns the Element for this toolbar.
28341      * @return {Roo.Element}
28342      */
28343     getEl : function(){
28344         return this.el;  
28345     },
28346     
28347     /**
28348      * Adds a separator
28349      * @return {Roo.Toolbar.Item} The separator item
28350      */
28351     addSeparator : function(){
28352         return this.addItem(new Roo.Toolbar.Separator());
28353     },
28354
28355     /**
28356      * Adds a spacer element
28357      * @return {Roo.Toolbar.Spacer} The spacer item
28358      */
28359     addSpacer : function(){
28360         return this.addItem(new Roo.Toolbar.Spacer());
28361     },
28362
28363     /**
28364      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28365      * @return {Roo.Toolbar.Fill} The fill item
28366      */
28367     addFill : function(){
28368         return this.addItem(new Roo.Toolbar.Fill());
28369     },
28370
28371     /**
28372      * Adds any standard HTML element to the toolbar
28373      * @param {String/HTMLElement/Element} el The element or id of the element to add
28374      * @return {Roo.Toolbar.Item} The element's item
28375      */
28376     addElement : function(el){
28377         return this.addItem(new Roo.Toolbar.Item(el));
28378     },
28379     /**
28380      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28381      * @type Roo.util.MixedCollection  
28382      */
28383     items : false,
28384      
28385     /**
28386      * Adds any Toolbar.Item or subclass
28387      * @param {Roo.Toolbar.Item} item
28388      * @return {Roo.Toolbar.Item} The item
28389      */
28390     addItem : function(item){
28391         var td = this.nextBlock();
28392         item.render(td);
28393         this.items.add(item);
28394         return item;
28395     },
28396     
28397     /**
28398      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28399      * @param {Object/Array} config A button config or array of configs
28400      * @return {Roo.Toolbar.Button/Array}
28401      */
28402     addButton : function(config){
28403         if(config instanceof Array){
28404             var buttons = [];
28405             for(var i = 0, len = config.length; i < len; i++) {
28406                 buttons.push(this.addButton(config[i]));
28407             }
28408             return buttons;
28409         }
28410         var b = config;
28411         if(!(config instanceof Roo.Toolbar.Button)){
28412             b = config.split ?
28413                 new Roo.Toolbar.SplitButton(config) :
28414                 new Roo.Toolbar.Button(config);
28415         }
28416         var td = this.nextBlock();
28417         b.render(td);
28418         this.items.add(b);
28419         return b;
28420     },
28421     
28422     /**
28423      * Adds text to the toolbar
28424      * @param {String} text The text to add
28425      * @return {Roo.Toolbar.Item} The element's item
28426      */
28427     addText : function(text){
28428         return this.addItem(new Roo.Toolbar.TextItem(text));
28429     },
28430     
28431     /**
28432      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28433      * @param {Number} index The index where the item is to be inserted
28434      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28435      * @return {Roo.Toolbar.Button/Item}
28436      */
28437     insertButton : function(index, item){
28438         if(item instanceof Array){
28439             var buttons = [];
28440             for(var i = 0, len = item.length; i < len; i++) {
28441                buttons.push(this.insertButton(index + i, item[i]));
28442             }
28443             return buttons;
28444         }
28445         if (!(item instanceof Roo.Toolbar.Button)){
28446            item = new Roo.Toolbar.Button(item);
28447         }
28448         var td = document.createElement("td");
28449         this.tr.insertBefore(td, this.tr.childNodes[index]);
28450         item.render(td);
28451         this.items.insert(index, item);
28452         return item;
28453     },
28454     
28455     /**
28456      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28457      * @param {Object} config
28458      * @return {Roo.Toolbar.Item} The element's item
28459      */
28460     addDom : function(config, returnEl){
28461         var td = this.nextBlock();
28462         Roo.DomHelper.overwrite(td, config);
28463         var ti = new Roo.Toolbar.Item(td.firstChild);
28464         ti.render(td);
28465         this.items.add(ti);
28466         return ti;
28467     },
28468
28469     /**
28470      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28471      * @type Roo.util.MixedCollection  
28472      */
28473     fields : false,
28474     
28475     /**
28476      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28477      * Note: the field should not have been rendered yet. For a field that has already been
28478      * rendered, use {@link #addElement}.
28479      * @param {Roo.form.Field} field
28480      * @return {Roo.ToolbarItem}
28481      */
28482      
28483       
28484     addField : function(field) {
28485         if (!this.fields) {
28486             var autoId = 0;
28487             this.fields = new Roo.util.MixedCollection(false, function(o){
28488                 return o.id || ("item" + (++autoId));
28489             });
28490
28491         }
28492         
28493         var td = this.nextBlock();
28494         field.render(td);
28495         var ti = new Roo.Toolbar.Item(td.firstChild);
28496         ti.render(td);
28497         this.items.add(ti);
28498         this.fields.add(field);
28499         return ti;
28500     },
28501     /**
28502      * Hide the toolbar
28503      * @method hide
28504      */
28505      
28506       
28507     hide : function()
28508     {
28509         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28510         this.el.child('div').hide();
28511     },
28512     /**
28513      * Show the toolbar
28514      * @method show
28515      */
28516     show : function()
28517     {
28518         this.el.child('div').show();
28519     },
28520       
28521     // private
28522     nextBlock : function(){
28523         var td = document.createElement("td");
28524         this.tr.appendChild(td);
28525         return td;
28526     },
28527
28528     // private
28529     destroy : function(){
28530         if(this.items){ // rendered?
28531             Roo.destroy.apply(Roo, this.items.items);
28532         }
28533         if(this.fields){ // rendered?
28534             Roo.destroy.apply(Roo, this.fields.items);
28535         }
28536         Roo.Element.uncache(this.el, this.tr);
28537     }
28538 };
28539
28540 /**
28541  * @class Roo.Toolbar.Item
28542  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28543  * @constructor
28544  * Creates a new Item
28545  * @param {HTMLElement} el 
28546  */
28547 Roo.Toolbar.Item = function(el){
28548     var cfg = {};
28549     if (typeof (el.xtype) != 'undefined') {
28550         cfg = el;
28551         el = cfg.el;
28552     }
28553     
28554     this.el = Roo.getDom(el);
28555     this.id = Roo.id(this.el);
28556     this.hidden = false;
28557     
28558     this.addEvents({
28559          /**
28560              * @event render
28561              * Fires when the button is rendered
28562              * @param {Button} this
28563              */
28564         'render': true
28565     });
28566     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28567 };
28568 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28569 //Roo.Toolbar.Item.prototype = {
28570     
28571     /**
28572      * Get this item's HTML Element
28573      * @return {HTMLElement}
28574      */
28575     getEl : function(){
28576        return this.el;  
28577     },
28578
28579     // private
28580     render : function(td){
28581         
28582          this.td = td;
28583         td.appendChild(this.el);
28584         
28585         this.fireEvent('render', this);
28586     },
28587     
28588     /**
28589      * Removes and destroys this item.
28590      */
28591     destroy : function(){
28592         this.td.parentNode.removeChild(this.td);
28593     },
28594     
28595     /**
28596      * Shows this item.
28597      */
28598     show: function(){
28599         this.hidden = false;
28600         this.td.style.display = "";
28601     },
28602     
28603     /**
28604      * Hides this item.
28605      */
28606     hide: function(){
28607         this.hidden = true;
28608         this.td.style.display = "none";
28609     },
28610     
28611     /**
28612      * Convenience function for boolean show/hide.
28613      * @param {Boolean} visible true to show/false to hide
28614      */
28615     setVisible: function(visible){
28616         if(visible) {
28617             this.show();
28618         }else{
28619             this.hide();
28620         }
28621     },
28622     
28623     /**
28624      * Try to focus this item.
28625      */
28626     focus : function(){
28627         Roo.fly(this.el).focus();
28628     },
28629     
28630     /**
28631      * Disables this item.
28632      */
28633     disable : function(){
28634         Roo.fly(this.td).addClass("x-item-disabled");
28635         this.disabled = true;
28636         this.el.disabled = true;
28637     },
28638     
28639     /**
28640      * Enables this item.
28641      */
28642     enable : function(){
28643         Roo.fly(this.td).removeClass("x-item-disabled");
28644         this.disabled = false;
28645         this.el.disabled = false;
28646     }
28647 });
28648
28649
28650 /**
28651  * @class Roo.Toolbar.Separator
28652  * @extends Roo.Toolbar.Item
28653  * A simple toolbar separator class
28654  * @constructor
28655  * Creates a new Separator
28656  */
28657 Roo.Toolbar.Separator = function(cfg){
28658     
28659     var s = document.createElement("span");
28660     s.className = "ytb-sep";
28661     if (cfg) {
28662         cfg.el = s;
28663     }
28664     
28665     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28666 };
28667 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28668     enable:Roo.emptyFn,
28669     disable:Roo.emptyFn,
28670     focus:Roo.emptyFn
28671 });
28672
28673 /**
28674  * @class Roo.Toolbar.Spacer
28675  * @extends Roo.Toolbar.Item
28676  * A simple element that adds extra horizontal space to a toolbar.
28677  * @constructor
28678  * Creates a new Spacer
28679  */
28680 Roo.Toolbar.Spacer = function(cfg){
28681     var s = document.createElement("div");
28682     s.className = "ytb-spacer";
28683     if (cfg) {
28684         cfg.el = s;
28685     }
28686     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28687 };
28688 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28689     enable:Roo.emptyFn,
28690     disable:Roo.emptyFn,
28691     focus:Roo.emptyFn
28692 });
28693
28694 /**
28695  * @class Roo.Toolbar.Fill
28696  * @extends Roo.Toolbar.Spacer
28697  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28698  * @constructor
28699  * Creates a new Spacer
28700  */
28701 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28702     // private
28703     render : function(td){
28704         td.style.width = '100%';
28705         Roo.Toolbar.Fill.superclass.render.call(this, td);
28706     }
28707 });
28708
28709 /**
28710  * @class Roo.Toolbar.TextItem
28711  * @extends Roo.Toolbar.Item
28712  * A simple class that renders text directly into a toolbar.
28713  * @constructor
28714  * Creates a new TextItem
28715  * @param {String} text
28716  */
28717 Roo.Toolbar.TextItem = function(cfg){
28718     var  text = cfg || "";
28719     if (typeof(cfg) == 'object') {
28720         text = cfg.text || "";
28721     }  else {
28722         cfg = null;
28723     }
28724     var s = document.createElement("span");
28725     s.className = "ytb-text";
28726     s.innerHTML = text;
28727     if (cfg) {
28728         cfg.el  = s;
28729     }
28730     
28731     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28732 };
28733 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28734     
28735      
28736     enable:Roo.emptyFn,
28737     disable:Roo.emptyFn,
28738     focus:Roo.emptyFn
28739 });
28740
28741 /**
28742  * @class Roo.Toolbar.Button
28743  * @extends Roo.Button
28744  * A button that renders into a toolbar.
28745  * @constructor
28746  * Creates a new Button
28747  * @param {Object} config A standard {@link Roo.Button} config object
28748  */
28749 Roo.Toolbar.Button = function(config){
28750     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28751 };
28752 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28753     render : function(td){
28754         this.td = td;
28755         Roo.Toolbar.Button.superclass.render.call(this, td);
28756     },
28757     
28758     /**
28759      * Removes and destroys this button
28760      */
28761     destroy : function(){
28762         Roo.Toolbar.Button.superclass.destroy.call(this);
28763         this.td.parentNode.removeChild(this.td);
28764     },
28765     
28766     /**
28767      * Shows this button
28768      */
28769     show: function(){
28770         this.hidden = false;
28771         this.td.style.display = "";
28772     },
28773     
28774     /**
28775      * Hides this button
28776      */
28777     hide: function(){
28778         this.hidden = true;
28779         this.td.style.display = "none";
28780     },
28781
28782     /**
28783      * Disables this item
28784      */
28785     disable : function(){
28786         Roo.fly(this.td).addClass("x-item-disabled");
28787         this.disabled = true;
28788     },
28789
28790     /**
28791      * Enables this item
28792      */
28793     enable : function(){
28794         Roo.fly(this.td).removeClass("x-item-disabled");
28795         this.disabled = false;
28796     }
28797 });
28798 // backwards compat
28799 Roo.ToolbarButton = Roo.Toolbar.Button;
28800
28801 /**
28802  * @class Roo.Toolbar.SplitButton
28803  * @extends Roo.SplitButton
28804  * A menu button that renders into a toolbar.
28805  * @constructor
28806  * Creates a new SplitButton
28807  * @param {Object} config A standard {@link Roo.SplitButton} config object
28808  */
28809 Roo.Toolbar.SplitButton = function(config){
28810     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28811 };
28812 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28813     render : function(td){
28814         this.td = td;
28815         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28816     },
28817     
28818     /**
28819      * Removes and destroys this button
28820      */
28821     destroy : function(){
28822         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28823         this.td.parentNode.removeChild(this.td);
28824     },
28825     
28826     /**
28827      * Shows this button
28828      */
28829     show: function(){
28830         this.hidden = false;
28831         this.td.style.display = "";
28832     },
28833     
28834     /**
28835      * Hides this button
28836      */
28837     hide: function(){
28838         this.hidden = true;
28839         this.td.style.display = "none";
28840     }
28841 });
28842
28843 // backwards compat
28844 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28845  * Based on:
28846  * Ext JS Library 1.1.1
28847  * Copyright(c) 2006-2007, Ext JS, LLC.
28848  *
28849  * Originally Released Under LGPL - original licence link has changed is not relivant.
28850  *
28851  * Fork - LGPL
28852  * <script type="text/javascript">
28853  */
28854  
28855 /**
28856  * @class Roo.PagingToolbar
28857  * @extends Roo.Toolbar
28858  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28859  * @constructor
28860  * Create a new PagingToolbar
28861  * @param {Object} config The config object
28862  */
28863 Roo.PagingToolbar = function(el, ds, config)
28864 {
28865     // old args format still supported... - xtype is prefered..
28866     if (typeof(el) == 'object' && el.xtype) {
28867         // created from xtype...
28868         config = el;
28869         ds = el.dataSource;
28870         el = config.container;
28871     }
28872     var items = [];
28873     if (config.items) {
28874         items = config.items;
28875         config.items = [];
28876     }
28877     
28878     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28879     this.ds = ds;
28880     this.cursor = 0;
28881     this.renderButtons(this.el);
28882     this.bind(ds);
28883     
28884     // supprot items array.
28885    
28886     Roo.each(items, function(e) {
28887         this.add(Roo.factory(e));
28888     },this);
28889     
28890 };
28891
28892 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28893     /**
28894      * @cfg {Roo.data.Store} dataSource
28895      * The underlying data store providing the paged data
28896      */
28897     /**
28898      * @cfg {String/HTMLElement/Element} container
28899      * container The id or element that will contain the toolbar
28900      */
28901     /**
28902      * @cfg {Boolean} displayInfo
28903      * True to display the displayMsg (defaults to false)
28904      */
28905     /**
28906      * @cfg {Number} pageSize
28907      * The number of records to display per page (defaults to 20)
28908      */
28909     pageSize: 20,
28910     /**
28911      * @cfg {String} displayMsg
28912      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28913      */
28914     displayMsg : 'Displaying {0} - {1} of {2}',
28915     /**
28916      * @cfg {String} emptyMsg
28917      * The message to display when no records are found (defaults to "No data to display")
28918      */
28919     emptyMsg : 'No data to display',
28920     /**
28921      * Customizable piece of the default paging text (defaults to "Page")
28922      * @type String
28923      */
28924     beforePageText : "Page",
28925     /**
28926      * Customizable piece of the default paging text (defaults to "of %0")
28927      * @type String
28928      */
28929     afterPageText : "of {0}",
28930     /**
28931      * Customizable piece of the default paging text (defaults to "First Page")
28932      * @type String
28933      */
28934     firstText : "First Page",
28935     /**
28936      * Customizable piece of the default paging text (defaults to "Previous Page")
28937      * @type String
28938      */
28939     prevText : "Previous Page",
28940     /**
28941      * Customizable piece of the default paging text (defaults to "Next Page")
28942      * @type String
28943      */
28944     nextText : "Next Page",
28945     /**
28946      * Customizable piece of the default paging text (defaults to "Last Page")
28947      * @type String
28948      */
28949     lastText : "Last Page",
28950     /**
28951      * Customizable piece of the default paging text (defaults to "Refresh")
28952      * @type String
28953      */
28954     refreshText : "Refresh",
28955
28956     // private
28957     renderButtons : function(el){
28958         Roo.PagingToolbar.superclass.render.call(this, el);
28959         this.first = this.addButton({
28960             tooltip: this.firstText,
28961             cls: "x-btn-icon x-grid-page-first",
28962             disabled: true,
28963             handler: this.onClick.createDelegate(this, ["first"])
28964         });
28965         this.prev = this.addButton({
28966             tooltip: this.prevText,
28967             cls: "x-btn-icon x-grid-page-prev",
28968             disabled: true,
28969             handler: this.onClick.createDelegate(this, ["prev"])
28970         });
28971         //this.addSeparator();
28972         this.add(this.beforePageText);
28973         this.field = Roo.get(this.addDom({
28974            tag: "input",
28975            type: "text",
28976            size: "3",
28977            value: "1",
28978            cls: "x-grid-page-number"
28979         }).el);
28980         this.field.on("keydown", this.onPagingKeydown, this);
28981         this.field.on("focus", function(){this.dom.select();});
28982         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28983         this.field.setHeight(18);
28984         //this.addSeparator();
28985         this.next = this.addButton({
28986             tooltip: this.nextText,
28987             cls: "x-btn-icon x-grid-page-next",
28988             disabled: true,
28989             handler: this.onClick.createDelegate(this, ["next"])
28990         });
28991         this.last = this.addButton({
28992             tooltip: this.lastText,
28993             cls: "x-btn-icon x-grid-page-last",
28994             disabled: true,
28995             handler: this.onClick.createDelegate(this, ["last"])
28996         });
28997         //this.addSeparator();
28998         this.loading = this.addButton({
28999             tooltip: this.refreshText,
29000             cls: "x-btn-icon x-grid-loading",
29001             handler: this.onClick.createDelegate(this, ["refresh"])
29002         });
29003
29004         if(this.displayInfo){
29005             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
29006         }
29007     },
29008
29009     // private
29010     updateInfo : function(){
29011         if(this.displayEl){
29012             var count = this.ds.getCount();
29013             var msg = count == 0 ?
29014                 this.emptyMsg :
29015                 String.format(
29016                     this.displayMsg,
29017                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29018                 );
29019             this.displayEl.update(msg);
29020         }
29021     },
29022
29023     // private
29024     onLoad : function(ds, r, o){
29025        this.cursor = o.params ? o.params.start : 0;
29026        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29027
29028        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29029        this.field.dom.value = ap;
29030        this.first.setDisabled(ap == 1);
29031        this.prev.setDisabled(ap == 1);
29032        this.next.setDisabled(ap == ps);
29033        this.last.setDisabled(ap == ps);
29034        this.loading.enable();
29035        this.updateInfo();
29036     },
29037
29038     // private
29039     getPageData : function(){
29040         var total = this.ds.getTotalCount();
29041         return {
29042             total : total,
29043             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29044             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29045         };
29046     },
29047
29048     // private
29049     onLoadError : function(){
29050         this.loading.enable();
29051     },
29052
29053     // private
29054     onPagingKeydown : function(e){
29055         var k = e.getKey();
29056         var d = this.getPageData();
29057         if(k == e.RETURN){
29058             var v = this.field.dom.value, pageNum;
29059             if(!v || isNaN(pageNum = parseInt(v, 10))){
29060                 this.field.dom.value = d.activePage;
29061                 return;
29062             }
29063             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29064             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29065             e.stopEvent();
29066         }
29067         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))
29068         {
29069           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29070           this.field.dom.value = pageNum;
29071           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29072           e.stopEvent();
29073         }
29074         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29075         {
29076           var v = this.field.dom.value, pageNum; 
29077           var increment = (e.shiftKey) ? 10 : 1;
29078           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29079             increment *= -1;
29080           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29081             this.field.dom.value = d.activePage;
29082             return;
29083           }
29084           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29085           {
29086             this.field.dom.value = parseInt(v, 10) + increment;
29087             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29088             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29089           }
29090           e.stopEvent();
29091         }
29092     },
29093
29094     // private
29095     beforeLoad : function(){
29096         if(this.loading){
29097             this.loading.disable();
29098         }
29099     },
29100
29101     // private
29102     onClick : function(which){
29103         var ds = this.ds;
29104         switch(which){
29105             case "first":
29106                 ds.load({params:{start: 0, limit: this.pageSize}});
29107             break;
29108             case "prev":
29109                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29110             break;
29111             case "next":
29112                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29113             break;
29114             case "last":
29115                 var total = ds.getTotalCount();
29116                 var extra = total % this.pageSize;
29117                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29118                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29119             break;
29120             case "refresh":
29121                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29122             break;
29123         }
29124     },
29125
29126     /**
29127      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29128      * @param {Roo.data.Store} store The data store to unbind
29129      */
29130     unbind : function(ds){
29131         ds.un("beforeload", this.beforeLoad, this);
29132         ds.un("load", this.onLoad, this);
29133         ds.un("loadexception", this.onLoadError, this);
29134         ds.un("remove", this.updateInfo, this);
29135         ds.un("add", this.updateInfo, this);
29136         this.ds = undefined;
29137     },
29138
29139     /**
29140      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29141      * @param {Roo.data.Store} store The data store to bind
29142      */
29143     bind : function(ds){
29144         ds.on("beforeload", this.beforeLoad, this);
29145         ds.on("load", this.onLoad, this);
29146         ds.on("loadexception", this.onLoadError, this);
29147         ds.on("remove", this.updateInfo, this);
29148         ds.on("add", this.updateInfo, this);
29149         this.ds = ds;
29150     }
29151 });/*
29152  * Based on:
29153  * Ext JS Library 1.1.1
29154  * Copyright(c) 2006-2007, Ext JS, LLC.
29155  *
29156  * Originally Released Under LGPL - original licence link has changed is not relivant.
29157  *
29158  * Fork - LGPL
29159  * <script type="text/javascript">
29160  */
29161
29162 /**
29163  * @class Roo.Resizable
29164  * @extends Roo.util.Observable
29165  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29166  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29167  * 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
29168  * the element will be wrapped for you automatically.</p>
29169  * <p>Here is the list of valid resize handles:</p>
29170  * <pre>
29171 Value   Description
29172 ------  -------------------
29173  'n'     north
29174  's'     south
29175  'e'     east
29176  'w'     west
29177  'nw'    northwest
29178  'sw'    southwest
29179  'se'    southeast
29180  'ne'    northeast
29181  'hd'    horizontal drag
29182  'all'   all
29183 </pre>
29184  * <p>Here's an example showing the creation of a typical Resizable:</p>
29185  * <pre><code>
29186 var resizer = new Roo.Resizable("element-id", {
29187     handles: 'all',
29188     minWidth: 200,
29189     minHeight: 100,
29190     maxWidth: 500,
29191     maxHeight: 400,
29192     pinned: true
29193 });
29194 resizer.on("resize", myHandler);
29195 </code></pre>
29196  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29197  * resizer.east.setDisplayed(false);</p>
29198  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29199  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29200  * resize operation's new size (defaults to [0, 0])
29201  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29202  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29203  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29204  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29205  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29206  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29207  * @cfg {Number} width The width of the element in pixels (defaults to null)
29208  * @cfg {Number} height The height of the element in pixels (defaults to null)
29209  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29210  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29211  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29212  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29213  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29214  * in favor of the handles config option (defaults to false)
29215  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29216  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29217  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29218  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29219  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29220  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29221  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29222  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29223  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29224  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29225  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29226  * @constructor
29227  * Create a new resizable component
29228  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29229  * @param {Object} config configuration options
29230   */
29231 Roo.Resizable = function(el, config)
29232 {
29233     this.el = Roo.get(el);
29234
29235     if(config && config.wrap){
29236         config.resizeChild = this.el;
29237         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29238         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29239         this.el.setStyle("overflow", "hidden");
29240         this.el.setPositioning(config.resizeChild.getPositioning());
29241         config.resizeChild.clearPositioning();
29242         if(!config.width || !config.height){
29243             var csize = config.resizeChild.getSize();
29244             this.el.setSize(csize.width, csize.height);
29245         }
29246         if(config.pinned && !config.adjustments){
29247             config.adjustments = "auto";
29248         }
29249     }
29250
29251     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29252     this.proxy.unselectable();
29253     this.proxy.enableDisplayMode('block');
29254
29255     Roo.apply(this, config);
29256
29257     if(this.pinned){
29258         this.disableTrackOver = true;
29259         this.el.addClass("x-resizable-pinned");
29260     }
29261     // if the element isn't positioned, make it relative
29262     var position = this.el.getStyle("position");
29263     if(position != "absolute" && position != "fixed"){
29264         this.el.setStyle("position", "relative");
29265     }
29266     if(!this.handles){ // no handles passed, must be legacy style
29267         this.handles = 's,e,se';
29268         if(this.multiDirectional){
29269             this.handles += ',n,w';
29270         }
29271     }
29272     if(this.handles == "all"){
29273         this.handles = "n s e w ne nw se sw";
29274     }
29275     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29276     var ps = Roo.Resizable.positions;
29277     for(var i = 0, len = hs.length; i < len; i++){
29278         if(hs[i] && ps[hs[i]]){
29279             var pos = ps[hs[i]];
29280             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29281         }
29282     }
29283     // legacy
29284     this.corner = this.southeast;
29285     
29286     // updateBox = the box can move..
29287     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29288         this.updateBox = true;
29289     }
29290
29291     this.activeHandle = null;
29292
29293     if(this.resizeChild){
29294         if(typeof this.resizeChild == "boolean"){
29295             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29296         }else{
29297             this.resizeChild = Roo.get(this.resizeChild, true);
29298         }
29299     }
29300     
29301     if(this.adjustments == "auto"){
29302         var rc = this.resizeChild;
29303         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29304         if(rc && (hw || hn)){
29305             rc.position("relative");
29306             rc.setLeft(hw ? hw.el.getWidth() : 0);
29307             rc.setTop(hn ? hn.el.getHeight() : 0);
29308         }
29309         this.adjustments = [
29310             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29311             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29312         ];
29313     }
29314
29315     if(this.draggable){
29316         this.dd = this.dynamic ?
29317             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29318         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29319     }
29320
29321     // public events
29322     this.addEvents({
29323         /**
29324          * @event beforeresize
29325          * Fired before resize is allowed. Set enabled to false to cancel resize.
29326          * @param {Roo.Resizable} this
29327          * @param {Roo.EventObject} e The mousedown event
29328          */
29329         "beforeresize" : true,
29330         /**
29331          * @event resizing
29332          * Fired a resizing.
29333          * @param {Roo.Resizable} this
29334          * @param {Number} x The new x position
29335          * @param {Number} y The new y position
29336          * @param {Number} w The new w width
29337          * @param {Number} h The new h hight
29338          * @param {Roo.EventObject} e The mouseup event
29339          */
29340         "resizing" : true,
29341         /**
29342          * @event resize
29343          * Fired after a resize.
29344          * @param {Roo.Resizable} this
29345          * @param {Number} width The new width
29346          * @param {Number} height The new height
29347          * @param {Roo.EventObject} e The mouseup event
29348          */
29349         "resize" : true
29350     });
29351
29352     if(this.width !== null && this.height !== null){
29353         this.resizeTo(this.width, this.height);
29354     }else{
29355         this.updateChildSize();
29356     }
29357     if(Roo.isIE){
29358         this.el.dom.style.zoom = 1;
29359     }
29360     Roo.Resizable.superclass.constructor.call(this);
29361 };
29362
29363 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29364         resizeChild : false,
29365         adjustments : [0, 0],
29366         minWidth : 5,
29367         minHeight : 5,
29368         maxWidth : 10000,
29369         maxHeight : 10000,
29370         enabled : true,
29371         animate : false,
29372         duration : .35,
29373         dynamic : false,
29374         handles : false,
29375         multiDirectional : false,
29376         disableTrackOver : false,
29377         easing : 'easeOutStrong',
29378         widthIncrement : 0,
29379         heightIncrement : 0,
29380         pinned : false,
29381         width : null,
29382         height : null,
29383         preserveRatio : false,
29384         transparent: false,
29385         minX: 0,
29386         minY: 0,
29387         draggable: false,
29388
29389         /**
29390          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29391          */
29392         constrainTo: undefined,
29393         /**
29394          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29395          */
29396         resizeRegion: undefined,
29397
29398
29399     /**
29400      * Perform a manual resize
29401      * @param {Number} width
29402      * @param {Number} height
29403      */
29404     resizeTo : function(width, height){
29405         this.el.setSize(width, height);
29406         this.updateChildSize();
29407         this.fireEvent("resize", this, width, height, null);
29408     },
29409
29410     // private
29411     startSizing : function(e, handle){
29412         this.fireEvent("beforeresize", this, e);
29413         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29414
29415             if(!this.overlay){
29416                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29417                 this.overlay.unselectable();
29418                 this.overlay.enableDisplayMode("block");
29419                 this.overlay.on("mousemove", this.onMouseMove, this);
29420                 this.overlay.on("mouseup", this.onMouseUp, this);
29421             }
29422             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29423
29424             this.resizing = true;
29425             this.startBox = this.el.getBox();
29426             this.startPoint = e.getXY();
29427             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29428                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29429
29430             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29431             this.overlay.show();
29432
29433             if(this.constrainTo) {
29434                 var ct = Roo.get(this.constrainTo);
29435                 this.resizeRegion = ct.getRegion().adjust(
29436                     ct.getFrameWidth('t'),
29437                     ct.getFrameWidth('l'),
29438                     -ct.getFrameWidth('b'),
29439                     -ct.getFrameWidth('r')
29440                 );
29441             }
29442
29443             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29444             this.proxy.show();
29445             this.proxy.setBox(this.startBox);
29446             if(!this.dynamic){
29447                 this.proxy.setStyle('visibility', 'visible');
29448             }
29449         }
29450     },
29451
29452     // private
29453     onMouseDown : function(handle, e){
29454         if(this.enabled){
29455             e.stopEvent();
29456             this.activeHandle = handle;
29457             this.startSizing(e, handle);
29458         }
29459     },
29460
29461     // private
29462     onMouseUp : function(e){
29463         var size = this.resizeElement();
29464         this.resizing = false;
29465         this.handleOut();
29466         this.overlay.hide();
29467         this.proxy.hide();
29468         this.fireEvent("resize", this, size.width, size.height, e);
29469     },
29470
29471     // private
29472     updateChildSize : function(){
29473         
29474         if(this.resizeChild){
29475             var el = this.el;
29476             var child = this.resizeChild;
29477             var adj = this.adjustments;
29478             if(el.dom.offsetWidth){
29479                 var b = el.getSize(true);
29480                 child.setSize(b.width+adj[0], b.height+adj[1]);
29481             }
29482             // Second call here for IE
29483             // The first call enables instant resizing and
29484             // the second call corrects scroll bars if they
29485             // exist
29486             if(Roo.isIE){
29487                 setTimeout(function(){
29488                     if(el.dom.offsetWidth){
29489                         var b = el.getSize(true);
29490                         child.setSize(b.width+adj[0], b.height+adj[1]);
29491                     }
29492                 }, 10);
29493             }
29494         }
29495     },
29496
29497     // private
29498     snap : function(value, inc, min){
29499         if(!inc || !value) return value;
29500         var newValue = value;
29501         var m = value % inc;
29502         if(m > 0){
29503             if(m > (inc/2)){
29504                 newValue = value + (inc-m);
29505             }else{
29506                 newValue = value - m;
29507             }
29508         }
29509         return Math.max(min, newValue);
29510     },
29511
29512     // private
29513     resizeElement : function(){
29514         var box = this.proxy.getBox();
29515         if(this.updateBox){
29516             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29517         }else{
29518             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29519         }
29520         this.updateChildSize();
29521         if(!this.dynamic){
29522             this.proxy.hide();
29523         }
29524         return box;
29525     },
29526
29527     // private
29528     constrain : function(v, diff, m, mx){
29529         if(v - diff < m){
29530             diff = v - m;
29531         }else if(v - diff > mx){
29532             diff = mx - v;
29533         }
29534         return diff;
29535     },
29536
29537     // private
29538     onMouseMove : function(e){
29539         
29540         if(this.enabled){
29541             try{// try catch so if something goes wrong the user doesn't get hung
29542
29543             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29544                 return;
29545             }
29546
29547             //var curXY = this.startPoint;
29548             var curSize = this.curSize || this.startBox;
29549             var x = this.startBox.x, y = this.startBox.y;
29550             var ox = x, oy = y;
29551             var w = curSize.width, h = curSize.height;
29552             var ow = w, oh = h;
29553             var mw = this.minWidth, mh = this.minHeight;
29554             var mxw = this.maxWidth, mxh = this.maxHeight;
29555             var wi = this.widthIncrement;
29556             var hi = this.heightIncrement;
29557
29558             var eventXY = e.getXY();
29559             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29560             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29561
29562             var pos = this.activeHandle.position;
29563
29564             switch(pos){
29565                 case "east":
29566                     w += diffX;
29567                     w = Math.min(Math.max(mw, w), mxw);
29568                     break;
29569              
29570                 case "south":
29571                     h += diffY;
29572                     h = Math.min(Math.max(mh, h), mxh);
29573                     break;
29574                 case "southeast":
29575                     w += diffX;
29576                     h += diffY;
29577                     w = Math.min(Math.max(mw, w), mxw);
29578                     h = Math.min(Math.max(mh, h), mxh);
29579                     break;
29580                 case "north":
29581                     diffY = this.constrain(h, diffY, mh, mxh);
29582                     y += diffY;
29583                     h -= diffY;
29584                     break;
29585                 case "hdrag":
29586                     
29587                     if (wi) {
29588                         var adiffX = Math.abs(diffX);
29589                         var sub = (adiffX % wi); // how much 
29590                         if (sub > (wi/2)) { // far enough to snap
29591                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29592                         } else {
29593                             // remove difference.. 
29594                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29595                         }
29596                     }
29597                     x += diffX;
29598                     x = Math.max(this.minX, x);
29599                     break;
29600                 case "west":
29601                     diffX = this.constrain(w, diffX, mw, mxw);
29602                     x += diffX;
29603                     w -= diffX;
29604                     break;
29605                 case "northeast":
29606                     w += diffX;
29607                     w = Math.min(Math.max(mw, w), mxw);
29608                     diffY = this.constrain(h, diffY, mh, mxh);
29609                     y += diffY;
29610                     h -= diffY;
29611                     break;
29612                 case "northwest":
29613                     diffX = this.constrain(w, diffX, mw, mxw);
29614                     diffY = this.constrain(h, diffY, mh, mxh);
29615                     y += diffY;
29616                     h -= diffY;
29617                     x += diffX;
29618                     w -= diffX;
29619                     break;
29620                case "southwest":
29621                     diffX = this.constrain(w, diffX, mw, mxw);
29622                     h += diffY;
29623                     h = Math.min(Math.max(mh, h), mxh);
29624                     x += diffX;
29625                     w -= diffX;
29626                     break;
29627             }
29628
29629             var sw = this.snap(w, wi, mw);
29630             var sh = this.snap(h, hi, mh);
29631             if(sw != w || sh != h){
29632                 switch(pos){
29633                     case "northeast":
29634                         y -= sh - h;
29635                     break;
29636                     case "north":
29637                         y -= sh - h;
29638                         break;
29639                     case "southwest":
29640                         x -= sw - w;
29641                     break;
29642                     case "west":
29643                         x -= sw - w;
29644                         break;
29645                     case "northwest":
29646                         x -= sw - w;
29647                         y -= sh - h;
29648                     break;
29649                 }
29650                 w = sw;
29651                 h = sh;
29652             }
29653
29654             if(this.preserveRatio){
29655                 switch(pos){
29656                     case "southeast":
29657                     case "east":
29658                         h = oh * (w/ow);
29659                         h = Math.min(Math.max(mh, h), mxh);
29660                         w = ow * (h/oh);
29661                        break;
29662                     case "south":
29663                         w = ow * (h/oh);
29664                         w = Math.min(Math.max(mw, w), mxw);
29665                         h = oh * (w/ow);
29666                         break;
29667                     case "northeast":
29668                         w = ow * (h/oh);
29669                         w = Math.min(Math.max(mw, w), mxw);
29670                         h = oh * (w/ow);
29671                     break;
29672                     case "north":
29673                         var tw = w;
29674                         w = ow * (h/oh);
29675                         w = Math.min(Math.max(mw, w), mxw);
29676                         h = oh * (w/ow);
29677                         x += (tw - w) / 2;
29678                         break;
29679                     case "southwest":
29680                         h = oh * (w/ow);
29681                         h = Math.min(Math.max(mh, h), mxh);
29682                         var tw = w;
29683                         w = ow * (h/oh);
29684                         x += tw - w;
29685                         break;
29686                     case "west":
29687                         var th = h;
29688                         h = oh * (w/ow);
29689                         h = Math.min(Math.max(mh, h), mxh);
29690                         y += (th - h) / 2;
29691                         var tw = w;
29692                         w = ow * (h/oh);
29693                         x += tw - w;
29694                        break;
29695                     case "northwest":
29696                         var tw = w;
29697                         var th = h;
29698                         h = oh * (w/ow);
29699                         h = Math.min(Math.max(mh, h), mxh);
29700                         w = ow * (h/oh);
29701                         y += th - h;
29702                         x += tw - w;
29703                        break;
29704
29705                 }
29706             }
29707             if (pos == 'hdrag') {
29708                 w = ow;
29709             }
29710             this.proxy.setBounds(x, y, w, h);
29711             if(this.dynamic){
29712                 this.resizeElement();
29713             }
29714             }catch(e){}
29715         }
29716         this.fireEvent("resizing", this, x, y, w, h, e);
29717     },
29718
29719     // private
29720     handleOver : function(){
29721         if(this.enabled){
29722             this.el.addClass("x-resizable-over");
29723         }
29724     },
29725
29726     // private
29727     handleOut : function(){
29728         if(!this.resizing){
29729             this.el.removeClass("x-resizable-over");
29730         }
29731     },
29732
29733     /**
29734      * Returns the element this component is bound to.
29735      * @return {Roo.Element}
29736      */
29737     getEl : function(){
29738         return this.el;
29739     },
29740
29741     /**
29742      * Returns the resizeChild element (or null).
29743      * @return {Roo.Element}
29744      */
29745     getResizeChild : function(){
29746         return this.resizeChild;
29747     },
29748     groupHandler : function()
29749     {
29750         
29751     },
29752     /**
29753      * Destroys this resizable. If the element was wrapped and
29754      * removeEl is not true then the element remains.
29755      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29756      */
29757     destroy : function(removeEl){
29758         this.proxy.remove();
29759         if(this.overlay){
29760             this.overlay.removeAllListeners();
29761             this.overlay.remove();
29762         }
29763         var ps = Roo.Resizable.positions;
29764         for(var k in ps){
29765             if(typeof ps[k] != "function" && this[ps[k]]){
29766                 var h = this[ps[k]];
29767                 h.el.removeAllListeners();
29768                 h.el.remove();
29769             }
29770         }
29771         if(removeEl){
29772             this.el.update("");
29773             this.el.remove();
29774         }
29775     }
29776 });
29777
29778 // private
29779 // hash to map config positions to true positions
29780 Roo.Resizable.positions = {
29781     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29782     hd: "hdrag"
29783 };
29784
29785 // private
29786 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29787     if(!this.tpl){
29788         // only initialize the template if resizable is used
29789         var tpl = Roo.DomHelper.createTemplate(
29790             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29791         );
29792         tpl.compile();
29793         Roo.Resizable.Handle.prototype.tpl = tpl;
29794     }
29795     this.position = pos;
29796     this.rz = rz;
29797     // show north drag fro topdra
29798     var handlepos = pos == 'hdrag' ? 'north' : pos;
29799     
29800     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29801     if (pos == 'hdrag') {
29802         this.el.setStyle('cursor', 'pointer');
29803     }
29804     this.el.unselectable();
29805     if(transparent){
29806         this.el.setOpacity(0);
29807     }
29808     this.el.on("mousedown", this.onMouseDown, this);
29809     if(!disableTrackOver){
29810         this.el.on("mouseover", this.onMouseOver, this);
29811         this.el.on("mouseout", this.onMouseOut, this);
29812     }
29813 };
29814
29815 // private
29816 Roo.Resizable.Handle.prototype = {
29817     afterResize : function(rz){
29818         Roo.log('after?');
29819         // do nothing
29820     },
29821     // private
29822     onMouseDown : function(e){
29823         this.rz.onMouseDown(this, e);
29824     },
29825     // private
29826     onMouseOver : function(e){
29827         this.rz.handleOver(this, e);
29828     },
29829     // private
29830     onMouseOut : function(e){
29831         this.rz.handleOut(this, e);
29832     }
29833 };/*
29834  * Based on:
29835  * Ext JS Library 1.1.1
29836  * Copyright(c) 2006-2007, Ext JS, LLC.
29837  *
29838  * Originally Released Under LGPL - original licence link has changed is not relivant.
29839  *
29840  * Fork - LGPL
29841  * <script type="text/javascript">
29842  */
29843
29844 /**
29845  * @class Roo.Editor
29846  * @extends Roo.Component
29847  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29848  * @constructor
29849  * Create a new Editor
29850  * @param {Roo.form.Field} field The Field object (or descendant)
29851  * @param {Object} config The config object
29852  */
29853 Roo.Editor = function(field, config){
29854     Roo.Editor.superclass.constructor.call(this, config);
29855     this.field = field;
29856     this.addEvents({
29857         /**
29858              * @event beforestartedit
29859              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29860              * false from the handler of this event.
29861              * @param {Editor} this
29862              * @param {Roo.Element} boundEl The underlying element bound to this editor
29863              * @param {Mixed} value The field value being set
29864              */
29865         "beforestartedit" : true,
29866         /**
29867              * @event startedit
29868              * Fires when this editor is displayed
29869              * @param {Roo.Element} boundEl The underlying element bound to this editor
29870              * @param {Mixed} value The starting field value
29871              */
29872         "startedit" : true,
29873         /**
29874              * @event beforecomplete
29875              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29876              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29877              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29878              * event will not fire since no edit actually occurred.
29879              * @param {Editor} this
29880              * @param {Mixed} value The current field value
29881              * @param {Mixed} startValue The original field value
29882              */
29883         "beforecomplete" : true,
29884         /**
29885              * @event complete
29886              * Fires after editing is complete and any changed value has been written to the underlying field.
29887              * @param {Editor} this
29888              * @param {Mixed} value The current field value
29889              * @param {Mixed} startValue The original field value
29890              */
29891         "complete" : true,
29892         /**
29893          * @event specialkey
29894          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29895          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29896          * @param {Roo.form.Field} this
29897          * @param {Roo.EventObject} e The event object
29898          */
29899         "specialkey" : true
29900     });
29901 };
29902
29903 Roo.extend(Roo.Editor, Roo.Component, {
29904     /**
29905      * @cfg {Boolean/String} autosize
29906      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29907      * or "height" to adopt the height only (defaults to false)
29908      */
29909     /**
29910      * @cfg {Boolean} revertInvalid
29911      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29912      * validation fails (defaults to true)
29913      */
29914     /**
29915      * @cfg {Boolean} ignoreNoChange
29916      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29917      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29918      * will never be ignored.
29919      */
29920     /**
29921      * @cfg {Boolean} hideEl
29922      * False to keep the bound element visible while the editor is displayed (defaults to true)
29923      */
29924     /**
29925      * @cfg {Mixed} value
29926      * The data value of the underlying field (defaults to "")
29927      */
29928     value : "",
29929     /**
29930      * @cfg {String} alignment
29931      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29932      */
29933     alignment: "c-c?",
29934     /**
29935      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29936      * for bottom-right shadow (defaults to "frame")
29937      */
29938     shadow : "frame",
29939     /**
29940      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29941      */
29942     constrain : false,
29943     /**
29944      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29945      */
29946     completeOnEnter : false,
29947     /**
29948      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29949      */
29950     cancelOnEsc : false,
29951     /**
29952      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29953      */
29954     updateEl : false,
29955
29956     // private
29957     onRender : function(ct, position){
29958         this.el = new Roo.Layer({
29959             shadow: this.shadow,
29960             cls: "x-editor",
29961             parentEl : ct,
29962             shim : this.shim,
29963             shadowOffset:4,
29964             id: this.id,
29965             constrain: this.constrain
29966         });
29967         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29968         if(this.field.msgTarget != 'title'){
29969             this.field.msgTarget = 'qtip';
29970         }
29971         this.field.render(this.el);
29972         if(Roo.isGecko){
29973             this.field.el.dom.setAttribute('autocomplete', 'off');
29974         }
29975         this.field.on("specialkey", this.onSpecialKey, this);
29976         if(this.swallowKeys){
29977             this.field.el.swallowEvent(['keydown','keypress']);
29978         }
29979         this.field.show();
29980         this.field.on("blur", this.onBlur, this);
29981         if(this.field.grow){
29982             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29983         }
29984     },
29985
29986     onSpecialKey : function(field, e)
29987     {
29988         //Roo.log('editor onSpecialKey');
29989         if(this.completeOnEnter && e.getKey() == e.ENTER){
29990             e.stopEvent();
29991             this.completeEdit();
29992             return;
29993         }
29994         // do not fire special key otherwise it might hide close the editor...
29995         if(e.getKey() == e.ENTER){    
29996             return;
29997         }
29998         if(this.cancelOnEsc && e.getKey() == e.ESC){
29999             this.cancelEdit();
30000             return;
30001         } 
30002         this.fireEvent('specialkey', field, e);
30003     
30004     },
30005
30006     /**
30007      * Starts the editing process and shows the editor.
30008      * @param {String/HTMLElement/Element} el The element to edit
30009      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30010       * to the innerHTML of el.
30011      */
30012     startEdit : function(el, value){
30013         if(this.editing){
30014             this.completeEdit();
30015         }
30016         this.boundEl = Roo.get(el);
30017         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30018         if(!this.rendered){
30019             this.render(this.parentEl || document.body);
30020         }
30021         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30022             return;
30023         }
30024         this.startValue = v;
30025         this.field.setValue(v);
30026         if(this.autoSize){
30027             var sz = this.boundEl.getSize();
30028             switch(this.autoSize){
30029                 case "width":
30030                 this.setSize(sz.width,  "");
30031                 break;
30032                 case "height":
30033                 this.setSize("",  sz.height);
30034                 break;
30035                 default:
30036                 this.setSize(sz.width,  sz.height);
30037             }
30038         }
30039         this.el.alignTo(this.boundEl, this.alignment);
30040         this.editing = true;
30041         if(Roo.QuickTips){
30042             Roo.QuickTips.disable();
30043         }
30044         this.show();
30045     },
30046
30047     /**
30048      * Sets the height and width of this editor.
30049      * @param {Number} width The new width
30050      * @param {Number} height The new height
30051      */
30052     setSize : function(w, h){
30053         this.field.setSize(w, h);
30054         if(this.el){
30055             this.el.sync();
30056         }
30057     },
30058
30059     /**
30060      * Realigns the editor to the bound field based on the current alignment config value.
30061      */
30062     realign : function(){
30063         this.el.alignTo(this.boundEl, this.alignment);
30064     },
30065
30066     /**
30067      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30068      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30069      */
30070     completeEdit : function(remainVisible){
30071         if(!this.editing){
30072             return;
30073         }
30074         var v = this.getValue();
30075         if(this.revertInvalid !== false && !this.field.isValid()){
30076             v = this.startValue;
30077             this.cancelEdit(true);
30078         }
30079         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30080             this.editing = false;
30081             this.hide();
30082             return;
30083         }
30084         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30085             this.editing = false;
30086             if(this.updateEl && this.boundEl){
30087                 this.boundEl.update(v);
30088             }
30089             if(remainVisible !== true){
30090                 this.hide();
30091             }
30092             this.fireEvent("complete", this, v, this.startValue);
30093         }
30094     },
30095
30096     // private
30097     onShow : function(){
30098         this.el.show();
30099         if(this.hideEl !== false){
30100             this.boundEl.hide();
30101         }
30102         this.field.show();
30103         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30104             this.fixIEFocus = true;
30105             this.deferredFocus.defer(50, this);
30106         }else{
30107             this.field.focus();
30108         }
30109         this.fireEvent("startedit", this.boundEl, this.startValue);
30110     },
30111
30112     deferredFocus : function(){
30113         if(this.editing){
30114             this.field.focus();
30115         }
30116     },
30117
30118     /**
30119      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30120      * reverted to the original starting value.
30121      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30122      * cancel (defaults to false)
30123      */
30124     cancelEdit : function(remainVisible){
30125         if(this.editing){
30126             this.setValue(this.startValue);
30127             if(remainVisible !== true){
30128                 this.hide();
30129             }
30130         }
30131     },
30132
30133     // private
30134     onBlur : function(){
30135         if(this.allowBlur !== true && this.editing){
30136             this.completeEdit();
30137         }
30138     },
30139
30140     // private
30141     onHide : function(){
30142         if(this.editing){
30143             this.completeEdit();
30144             return;
30145         }
30146         this.field.blur();
30147         if(this.field.collapse){
30148             this.field.collapse();
30149         }
30150         this.el.hide();
30151         if(this.hideEl !== false){
30152             this.boundEl.show();
30153         }
30154         if(Roo.QuickTips){
30155             Roo.QuickTips.enable();
30156         }
30157     },
30158
30159     /**
30160      * Sets the data value of the editor
30161      * @param {Mixed} value Any valid value supported by the underlying field
30162      */
30163     setValue : function(v){
30164         this.field.setValue(v);
30165     },
30166
30167     /**
30168      * Gets the data value of the editor
30169      * @return {Mixed} The data value
30170      */
30171     getValue : function(){
30172         return this.field.getValue();
30173     }
30174 });/*
30175  * Based on:
30176  * Ext JS Library 1.1.1
30177  * Copyright(c) 2006-2007, Ext JS, LLC.
30178  *
30179  * Originally Released Under LGPL - original licence link has changed is not relivant.
30180  *
30181  * Fork - LGPL
30182  * <script type="text/javascript">
30183  */
30184  
30185 /**
30186  * @class Roo.BasicDialog
30187  * @extends Roo.util.Observable
30188  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30189  * <pre><code>
30190 var dlg = new Roo.BasicDialog("my-dlg", {
30191     height: 200,
30192     width: 300,
30193     minHeight: 100,
30194     minWidth: 150,
30195     modal: true,
30196     proxyDrag: true,
30197     shadow: true
30198 });
30199 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30200 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30201 dlg.addButton('Cancel', dlg.hide, dlg);
30202 dlg.show();
30203 </code></pre>
30204   <b>A Dialog should always be a direct child of the body element.</b>
30205  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30206  * @cfg {String} title Default text to display in the title bar (defaults to null)
30207  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30208  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30209  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30210  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30211  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30212  * (defaults to null with no animation)
30213  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30214  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30215  * property for valid values (defaults to 'all')
30216  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30217  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30218  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30219  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30220  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30221  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30222  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30223  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30224  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30225  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30226  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30227  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30228  * draggable = true (defaults to false)
30229  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30230  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30231  * shadow (defaults to false)
30232  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30233  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30234  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30235  * @cfg {Array} buttons Array of buttons
30236  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30237  * @constructor
30238  * Create a new BasicDialog.
30239  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30240  * @param {Object} config Configuration options
30241  */
30242 Roo.BasicDialog = function(el, config){
30243     this.el = Roo.get(el);
30244     var dh = Roo.DomHelper;
30245     if(!this.el && config && config.autoCreate){
30246         if(typeof config.autoCreate == "object"){
30247             if(!config.autoCreate.id){
30248                 config.autoCreate.id = el;
30249             }
30250             this.el = dh.append(document.body,
30251                         config.autoCreate, true);
30252         }else{
30253             this.el = dh.append(document.body,
30254                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30255         }
30256     }
30257     el = this.el;
30258     el.setDisplayed(true);
30259     el.hide = this.hideAction;
30260     this.id = el.id;
30261     el.addClass("x-dlg");
30262
30263     Roo.apply(this, config);
30264
30265     this.proxy = el.createProxy("x-dlg-proxy");
30266     this.proxy.hide = this.hideAction;
30267     this.proxy.setOpacity(.5);
30268     this.proxy.hide();
30269
30270     if(config.width){
30271         el.setWidth(config.width);
30272     }
30273     if(config.height){
30274         el.setHeight(config.height);
30275     }
30276     this.size = el.getSize();
30277     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30278         this.xy = [config.x,config.y];
30279     }else{
30280         this.xy = el.getCenterXY(true);
30281     }
30282     /** The header element @type Roo.Element */
30283     this.header = el.child("> .x-dlg-hd");
30284     /** The body element @type Roo.Element */
30285     this.body = el.child("> .x-dlg-bd");
30286     /** The footer element @type Roo.Element */
30287     this.footer = el.child("> .x-dlg-ft");
30288
30289     if(!this.header){
30290         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30291     }
30292     if(!this.body){
30293         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30294     }
30295
30296     this.header.unselectable();
30297     if(this.title){
30298         this.header.update(this.title);
30299     }
30300     // this element allows the dialog to be focused for keyboard event
30301     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30302     this.focusEl.swallowEvent("click", true);
30303
30304     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30305
30306     // wrap the body and footer for special rendering
30307     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30308     if(this.footer){
30309         this.bwrap.dom.appendChild(this.footer.dom);
30310     }
30311
30312     this.bg = this.el.createChild({
30313         tag: "div", cls:"x-dlg-bg",
30314         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30315     });
30316     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30317
30318
30319     if(this.autoScroll !== false && !this.autoTabs){
30320         this.body.setStyle("overflow", "auto");
30321     }
30322
30323     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30324
30325     if(this.closable !== false){
30326         this.el.addClass("x-dlg-closable");
30327         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30328         this.close.on("click", this.closeClick, this);
30329         this.close.addClassOnOver("x-dlg-close-over");
30330     }
30331     if(this.collapsible !== false){
30332         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30333         this.collapseBtn.on("click", this.collapseClick, this);
30334         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30335         this.header.on("dblclick", this.collapseClick, this);
30336     }
30337     if(this.resizable !== false){
30338         this.el.addClass("x-dlg-resizable");
30339         this.resizer = new Roo.Resizable(el, {
30340             minWidth: this.minWidth || 80,
30341             minHeight:this.minHeight || 80,
30342             handles: this.resizeHandles || "all",
30343             pinned: true
30344         });
30345         this.resizer.on("beforeresize", this.beforeResize, this);
30346         this.resizer.on("resize", this.onResize, this);
30347     }
30348     if(this.draggable !== false){
30349         el.addClass("x-dlg-draggable");
30350         if (!this.proxyDrag) {
30351             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30352         }
30353         else {
30354             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30355         }
30356         dd.setHandleElId(this.header.id);
30357         dd.endDrag = this.endMove.createDelegate(this);
30358         dd.startDrag = this.startMove.createDelegate(this);
30359         dd.onDrag = this.onDrag.createDelegate(this);
30360         dd.scroll = false;
30361         this.dd = dd;
30362     }
30363     if(this.modal){
30364         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30365         this.mask.enableDisplayMode("block");
30366         this.mask.hide();
30367         this.el.addClass("x-dlg-modal");
30368     }
30369     if(this.shadow){
30370         this.shadow = new Roo.Shadow({
30371             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30372             offset : this.shadowOffset
30373         });
30374     }else{
30375         this.shadowOffset = 0;
30376     }
30377     if(Roo.useShims && this.shim !== false){
30378         this.shim = this.el.createShim();
30379         this.shim.hide = this.hideAction;
30380         this.shim.hide();
30381     }else{
30382         this.shim = false;
30383     }
30384     if(this.autoTabs){
30385         this.initTabs();
30386     }
30387     if (this.buttons) { 
30388         var bts= this.buttons;
30389         this.buttons = [];
30390         Roo.each(bts, function(b) {
30391             this.addButton(b);
30392         }, this);
30393     }
30394     
30395     
30396     this.addEvents({
30397         /**
30398          * @event keydown
30399          * Fires when a key is pressed
30400          * @param {Roo.BasicDialog} this
30401          * @param {Roo.EventObject} e
30402          */
30403         "keydown" : true,
30404         /**
30405          * @event move
30406          * Fires when this dialog is moved by the user.
30407          * @param {Roo.BasicDialog} this
30408          * @param {Number} x The new page X
30409          * @param {Number} y The new page Y
30410          */
30411         "move" : true,
30412         /**
30413          * @event resize
30414          * Fires when this dialog is resized by the user.
30415          * @param {Roo.BasicDialog} this
30416          * @param {Number} width The new width
30417          * @param {Number} height The new height
30418          */
30419         "resize" : true,
30420         /**
30421          * @event beforehide
30422          * Fires before this dialog is hidden.
30423          * @param {Roo.BasicDialog} this
30424          */
30425         "beforehide" : true,
30426         /**
30427          * @event hide
30428          * Fires when this dialog is hidden.
30429          * @param {Roo.BasicDialog} this
30430          */
30431         "hide" : true,
30432         /**
30433          * @event beforeshow
30434          * Fires before this dialog is shown.
30435          * @param {Roo.BasicDialog} this
30436          */
30437         "beforeshow" : true,
30438         /**
30439          * @event show
30440          * Fires when this dialog is shown.
30441          * @param {Roo.BasicDialog} this
30442          */
30443         "show" : true
30444     });
30445     el.on("keydown", this.onKeyDown, this);
30446     el.on("mousedown", this.toFront, this);
30447     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30448     this.el.hide();
30449     Roo.DialogManager.register(this);
30450     Roo.BasicDialog.superclass.constructor.call(this);
30451 };
30452
30453 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30454     shadowOffset: Roo.isIE ? 6 : 5,
30455     minHeight: 80,
30456     minWidth: 200,
30457     minButtonWidth: 75,
30458     defaultButton: null,
30459     buttonAlign: "right",
30460     tabTag: 'div',
30461     firstShow: true,
30462
30463     /**
30464      * Sets the dialog title text
30465      * @param {String} text The title text to display
30466      * @return {Roo.BasicDialog} this
30467      */
30468     setTitle : function(text){
30469         this.header.update(text);
30470         return this;
30471     },
30472
30473     // private
30474     closeClick : function(){
30475         this.hide();
30476     },
30477
30478     // private
30479     collapseClick : function(){
30480         this[this.collapsed ? "expand" : "collapse"]();
30481     },
30482
30483     /**
30484      * Collapses the dialog to its minimized state (only the title bar is visible).
30485      * Equivalent to the user clicking the collapse dialog button.
30486      */
30487     collapse : function(){
30488         if(!this.collapsed){
30489             this.collapsed = true;
30490             this.el.addClass("x-dlg-collapsed");
30491             this.restoreHeight = this.el.getHeight();
30492             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30493         }
30494     },
30495
30496     /**
30497      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30498      * clicking the expand dialog button.
30499      */
30500     expand : function(){
30501         if(this.collapsed){
30502             this.collapsed = false;
30503             this.el.removeClass("x-dlg-collapsed");
30504             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30505         }
30506     },
30507
30508     /**
30509      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30510      * @return {Roo.TabPanel} The tabs component
30511      */
30512     initTabs : function(){
30513         var tabs = this.getTabs();
30514         while(tabs.getTab(0)){
30515             tabs.removeTab(0);
30516         }
30517         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30518             var dom = el.dom;
30519             tabs.addTab(Roo.id(dom), dom.title);
30520             dom.title = "";
30521         });
30522         tabs.activate(0);
30523         return tabs;
30524     },
30525
30526     // private
30527     beforeResize : function(){
30528         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30529     },
30530
30531     // private
30532     onResize : function(){
30533         this.refreshSize();
30534         this.syncBodyHeight();
30535         this.adjustAssets();
30536         this.focus();
30537         this.fireEvent("resize", this, this.size.width, this.size.height);
30538     },
30539
30540     // private
30541     onKeyDown : function(e){
30542         if(this.isVisible()){
30543             this.fireEvent("keydown", this, e);
30544         }
30545     },
30546
30547     /**
30548      * Resizes the dialog.
30549      * @param {Number} width
30550      * @param {Number} height
30551      * @return {Roo.BasicDialog} this
30552      */
30553     resizeTo : function(width, height){
30554         this.el.setSize(width, height);
30555         this.size = {width: width, height: height};
30556         this.syncBodyHeight();
30557         if(this.fixedcenter){
30558             this.center();
30559         }
30560         if(this.isVisible()){
30561             this.constrainXY();
30562             this.adjustAssets();
30563         }
30564         this.fireEvent("resize", this, width, height);
30565         return this;
30566     },
30567
30568
30569     /**
30570      * Resizes the dialog to fit the specified content size.
30571      * @param {Number} width
30572      * @param {Number} height
30573      * @return {Roo.BasicDialog} this
30574      */
30575     setContentSize : function(w, h){
30576         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30577         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30578         //if(!this.el.isBorderBox()){
30579             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30580             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30581         //}
30582         if(this.tabs){
30583             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30584             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30585         }
30586         this.resizeTo(w, h);
30587         return this;
30588     },
30589
30590     /**
30591      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30592      * executed in response to a particular key being pressed while the dialog is active.
30593      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30594      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30595      * @param {Function} fn The function to call
30596      * @param {Object} scope (optional) The scope of the function
30597      * @return {Roo.BasicDialog} this
30598      */
30599     addKeyListener : function(key, fn, scope){
30600         var keyCode, shift, ctrl, alt;
30601         if(typeof key == "object" && !(key instanceof Array)){
30602             keyCode = key["key"];
30603             shift = key["shift"];
30604             ctrl = key["ctrl"];
30605             alt = key["alt"];
30606         }else{
30607             keyCode = key;
30608         }
30609         var handler = function(dlg, e){
30610             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30611                 var k = e.getKey();
30612                 if(keyCode instanceof Array){
30613                     for(var i = 0, len = keyCode.length; i < len; i++){
30614                         if(keyCode[i] == k){
30615                           fn.call(scope || window, dlg, k, e);
30616                           return;
30617                         }
30618                     }
30619                 }else{
30620                     if(k == keyCode){
30621                         fn.call(scope || window, dlg, k, e);
30622                     }
30623                 }
30624             }
30625         };
30626         this.on("keydown", handler);
30627         return this;
30628     },
30629
30630     /**
30631      * Returns the TabPanel component (creates it if it doesn't exist).
30632      * Note: If you wish to simply check for the existence of tabs without creating them,
30633      * check for a null 'tabs' property.
30634      * @return {Roo.TabPanel} The tabs component
30635      */
30636     getTabs : function(){
30637         if(!this.tabs){
30638             this.el.addClass("x-dlg-auto-tabs");
30639             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30640             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30641         }
30642         return this.tabs;
30643     },
30644
30645     /**
30646      * Adds a button to the footer section of the dialog.
30647      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30648      * object or a valid Roo.DomHelper element config
30649      * @param {Function} handler The function called when the button is clicked
30650      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30651      * @return {Roo.Button} The new button
30652      */
30653     addButton : function(config, handler, scope){
30654         var dh = Roo.DomHelper;
30655         if(!this.footer){
30656             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30657         }
30658         if(!this.btnContainer){
30659             var tb = this.footer.createChild({
30660
30661                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30662                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30663             }, null, true);
30664             this.btnContainer = tb.firstChild.firstChild.firstChild;
30665         }
30666         var bconfig = {
30667             handler: handler,
30668             scope: scope,
30669             minWidth: this.minButtonWidth,
30670             hideParent:true
30671         };
30672         if(typeof config == "string"){
30673             bconfig.text = config;
30674         }else{
30675             if(config.tag){
30676                 bconfig.dhconfig = config;
30677             }else{
30678                 Roo.apply(bconfig, config);
30679             }
30680         }
30681         var fc = false;
30682         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30683             bconfig.position = Math.max(0, bconfig.position);
30684             fc = this.btnContainer.childNodes[bconfig.position];
30685         }
30686          
30687         var btn = new Roo.Button(
30688             fc ? 
30689                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30690                 : this.btnContainer.appendChild(document.createElement("td")),
30691             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30692             bconfig
30693         );
30694         this.syncBodyHeight();
30695         if(!this.buttons){
30696             /**
30697              * Array of all the buttons that have been added to this dialog via addButton
30698              * @type Array
30699              */
30700             this.buttons = [];
30701         }
30702         this.buttons.push(btn);
30703         return btn;
30704     },
30705
30706     /**
30707      * Sets the default button to be focused when the dialog is displayed.
30708      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30709      * @return {Roo.BasicDialog} this
30710      */
30711     setDefaultButton : function(btn){
30712         this.defaultButton = btn;
30713         return this;
30714     },
30715
30716     // private
30717     getHeaderFooterHeight : function(safe){
30718         var height = 0;
30719         if(this.header){
30720            height += this.header.getHeight();
30721         }
30722         if(this.footer){
30723            var fm = this.footer.getMargins();
30724             height += (this.footer.getHeight()+fm.top+fm.bottom);
30725         }
30726         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30727         height += this.centerBg.getPadding("tb");
30728         return height;
30729     },
30730
30731     // private
30732     syncBodyHeight : function()
30733     {
30734         var bd = this.body, // the text
30735             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30736             bw = this.bwrap;
30737         var height = this.size.height - this.getHeaderFooterHeight(false);
30738         bd.setHeight(height-bd.getMargins("tb"));
30739         var hh = this.header.getHeight();
30740         var h = this.size.height-hh;
30741         cb.setHeight(h);
30742         
30743         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30744         bw.setHeight(h-cb.getPadding("tb"));
30745         
30746         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30747         bd.setWidth(bw.getWidth(true));
30748         if(this.tabs){
30749             this.tabs.syncHeight();
30750             if(Roo.isIE){
30751                 this.tabs.el.repaint();
30752             }
30753         }
30754     },
30755
30756     /**
30757      * Restores the previous state of the dialog if Roo.state is configured.
30758      * @return {Roo.BasicDialog} this
30759      */
30760     restoreState : function(){
30761         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30762         if(box && box.width){
30763             this.xy = [box.x, box.y];
30764             this.resizeTo(box.width, box.height);
30765         }
30766         return this;
30767     },
30768
30769     // private
30770     beforeShow : function(){
30771         this.expand();
30772         if(this.fixedcenter){
30773             this.xy = this.el.getCenterXY(true);
30774         }
30775         if(this.modal){
30776             Roo.get(document.body).addClass("x-body-masked");
30777             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30778             this.mask.show();
30779         }
30780         this.constrainXY();
30781     },
30782
30783     // private
30784     animShow : function(){
30785         var b = Roo.get(this.animateTarget).getBox();
30786         this.proxy.setSize(b.width, b.height);
30787         this.proxy.setLocation(b.x, b.y);
30788         this.proxy.show();
30789         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30790                     true, .35, this.showEl.createDelegate(this));
30791     },
30792
30793     /**
30794      * Shows the dialog.
30795      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30796      * @return {Roo.BasicDialog} this
30797      */
30798     show : function(animateTarget){
30799         if (this.fireEvent("beforeshow", this) === false){
30800             return;
30801         }
30802         if(this.syncHeightBeforeShow){
30803             this.syncBodyHeight();
30804         }else if(this.firstShow){
30805             this.firstShow = false;
30806             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30807         }
30808         this.animateTarget = animateTarget || this.animateTarget;
30809         if(!this.el.isVisible()){
30810             this.beforeShow();
30811             if(this.animateTarget && Roo.get(this.animateTarget)){
30812                 this.animShow();
30813             }else{
30814                 this.showEl();
30815             }
30816         }
30817         return this;
30818     },
30819
30820     // private
30821     showEl : function(){
30822         this.proxy.hide();
30823         this.el.setXY(this.xy);
30824         this.el.show();
30825         this.adjustAssets(true);
30826         this.toFront();
30827         this.focus();
30828         // IE peekaboo bug - fix found by Dave Fenwick
30829         if(Roo.isIE){
30830             this.el.repaint();
30831         }
30832         this.fireEvent("show", this);
30833     },
30834
30835     /**
30836      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30837      * dialog itself will receive focus.
30838      */
30839     focus : function(){
30840         if(this.defaultButton){
30841             this.defaultButton.focus();
30842         }else{
30843             this.focusEl.focus();
30844         }
30845     },
30846
30847     // private
30848     constrainXY : function(){
30849         if(this.constraintoviewport !== false){
30850             if(!this.viewSize){
30851                 if(this.container){
30852                     var s = this.container.getSize();
30853                     this.viewSize = [s.width, s.height];
30854                 }else{
30855                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30856                 }
30857             }
30858             var s = Roo.get(this.container||document).getScroll();
30859
30860             var x = this.xy[0], y = this.xy[1];
30861             var w = this.size.width, h = this.size.height;
30862             var vw = this.viewSize[0], vh = this.viewSize[1];
30863             // only move it if it needs it
30864             var moved = false;
30865             // first validate right/bottom
30866             if(x + w > vw+s.left){
30867                 x = vw - w;
30868                 moved = true;
30869             }
30870             if(y + h > vh+s.top){
30871                 y = vh - h;
30872                 moved = true;
30873             }
30874             // then make sure top/left isn't negative
30875             if(x < s.left){
30876                 x = s.left;
30877                 moved = true;
30878             }
30879             if(y < s.top){
30880                 y = s.top;
30881                 moved = true;
30882             }
30883             if(moved){
30884                 // cache xy
30885                 this.xy = [x, y];
30886                 if(this.isVisible()){
30887                     this.el.setLocation(x, y);
30888                     this.adjustAssets();
30889                 }
30890             }
30891         }
30892     },
30893
30894     // private
30895     onDrag : function(){
30896         if(!this.proxyDrag){
30897             this.xy = this.el.getXY();
30898             this.adjustAssets();
30899         }
30900     },
30901
30902     // private
30903     adjustAssets : function(doShow){
30904         var x = this.xy[0], y = this.xy[1];
30905         var w = this.size.width, h = this.size.height;
30906         if(doShow === true){
30907             if(this.shadow){
30908                 this.shadow.show(this.el);
30909             }
30910             if(this.shim){
30911                 this.shim.show();
30912             }
30913         }
30914         if(this.shadow && this.shadow.isVisible()){
30915             this.shadow.show(this.el);
30916         }
30917         if(this.shim && this.shim.isVisible()){
30918             this.shim.setBounds(x, y, w, h);
30919         }
30920     },
30921
30922     // private
30923     adjustViewport : function(w, h){
30924         if(!w || !h){
30925             w = Roo.lib.Dom.getViewWidth();
30926             h = Roo.lib.Dom.getViewHeight();
30927         }
30928         // cache the size
30929         this.viewSize = [w, h];
30930         if(this.modal && this.mask.isVisible()){
30931             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30932             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30933         }
30934         if(this.isVisible()){
30935             this.constrainXY();
30936         }
30937     },
30938
30939     /**
30940      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30941      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30942      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30943      */
30944     destroy : function(removeEl){
30945         if(this.isVisible()){
30946             this.animateTarget = null;
30947             this.hide();
30948         }
30949         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30950         if(this.tabs){
30951             this.tabs.destroy(removeEl);
30952         }
30953         Roo.destroy(
30954              this.shim,
30955              this.proxy,
30956              this.resizer,
30957              this.close,
30958              this.mask
30959         );
30960         if(this.dd){
30961             this.dd.unreg();
30962         }
30963         if(this.buttons){
30964            for(var i = 0, len = this.buttons.length; i < len; i++){
30965                this.buttons[i].destroy();
30966            }
30967         }
30968         this.el.removeAllListeners();
30969         if(removeEl === true){
30970             this.el.update("");
30971             this.el.remove();
30972         }
30973         Roo.DialogManager.unregister(this);
30974     },
30975
30976     // private
30977     startMove : function(){
30978         if(this.proxyDrag){
30979             this.proxy.show();
30980         }
30981         if(this.constraintoviewport !== false){
30982             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30983         }
30984     },
30985
30986     // private
30987     endMove : function(){
30988         if(!this.proxyDrag){
30989             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30990         }else{
30991             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30992             this.proxy.hide();
30993         }
30994         this.refreshSize();
30995         this.adjustAssets();
30996         this.focus();
30997         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30998     },
30999
31000     /**
31001      * Brings this dialog to the front of any other visible dialogs
31002      * @return {Roo.BasicDialog} this
31003      */
31004     toFront : function(){
31005         Roo.DialogManager.bringToFront(this);
31006         return this;
31007     },
31008
31009     /**
31010      * Sends this dialog to the back (under) of any other visible dialogs
31011      * @return {Roo.BasicDialog} this
31012      */
31013     toBack : function(){
31014         Roo.DialogManager.sendToBack(this);
31015         return this;
31016     },
31017
31018     /**
31019      * Centers this dialog in the viewport
31020      * @return {Roo.BasicDialog} this
31021      */
31022     center : function(){
31023         var xy = this.el.getCenterXY(true);
31024         this.moveTo(xy[0], xy[1]);
31025         return this;
31026     },
31027
31028     /**
31029      * Moves the dialog's top-left corner to the specified point
31030      * @param {Number} x
31031      * @param {Number} y
31032      * @return {Roo.BasicDialog} this
31033      */
31034     moveTo : function(x, y){
31035         this.xy = [x,y];
31036         if(this.isVisible()){
31037             this.el.setXY(this.xy);
31038             this.adjustAssets();
31039         }
31040         return this;
31041     },
31042
31043     /**
31044      * Aligns the dialog to the specified element
31045      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31046      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31047      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31048      * @return {Roo.BasicDialog} this
31049      */
31050     alignTo : function(element, position, offsets){
31051         this.xy = this.el.getAlignToXY(element, position, offsets);
31052         if(this.isVisible()){
31053             this.el.setXY(this.xy);
31054             this.adjustAssets();
31055         }
31056         return this;
31057     },
31058
31059     /**
31060      * Anchors an element to another element and realigns it when the window is resized.
31061      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31062      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31063      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31064      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31065      * is a number, it is used as the buffer delay (defaults to 50ms).
31066      * @return {Roo.BasicDialog} this
31067      */
31068     anchorTo : function(el, alignment, offsets, monitorScroll){
31069         var action = function(){
31070             this.alignTo(el, alignment, offsets);
31071         };
31072         Roo.EventManager.onWindowResize(action, this);
31073         var tm = typeof monitorScroll;
31074         if(tm != 'undefined'){
31075             Roo.EventManager.on(window, 'scroll', action, this,
31076                 {buffer: tm == 'number' ? monitorScroll : 50});
31077         }
31078         action.call(this);
31079         return this;
31080     },
31081
31082     /**
31083      * Returns true if the dialog is visible
31084      * @return {Boolean}
31085      */
31086     isVisible : function(){
31087         return this.el.isVisible();
31088     },
31089
31090     // private
31091     animHide : function(callback){
31092         var b = Roo.get(this.animateTarget).getBox();
31093         this.proxy.show();
31094         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31095         this.el.hide();
31096         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31097                     this.hideEl.createDelegate(this, [callback]));
31098     },
31099
31100     /**
31101      * Hides the dialog.
31102      * @param {Function} callback (optional) Function to call when the dialog is hidden
31103      * @return {Roo.BasicDialog} this
31104      */
31105     hide : function(callback){
31106         if (this.fireEvent("beforehide", this) === false){
31107             return;
31108         }
31109         if(this.shadow){
31110             this.shadow.hide();
31111         }
31112         if(this.shim) {
31113           this.shim.hide();
31114         }
31115         // sometimes animateTarget seems to get set.. causing problems...
31116         // this just double checks..
31117         if(this.animateTarget && Roo.get(this.animateTarget)) {
31118            this.animHide(callback);
31119         }else{
31120             this.el.hide();
31121             this.hideEl(callback);
31122         }
31123         return this;
31124     },
31125
31126     // private
31127     hideEl : function(callback){
31128         this.proxy.hide();
31129         if(this.modal){
31130             this.mask.hide();
31131             Roo.get(document.body).removeClass("x-body-masked");
31132         }
31133         this.fireEvent("hide", this);
31134         if(typeof callback == "function"){
31135             callback();
31136         }
31137     },
31138
31139     // private
31140     hideAction : function(){
31141         this.setLeft("-10000px");
31142         this.setTop("-10000px");
31143         this.setStyle("visibility", "hidden");
31144     },
31145
31146     // private
31147     refreshSize : function(){
31148         this.size = this.el.getSize();
31149         this.xy = this.el.getXY();
31150         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31151     },
31152
31153     // private
31154     // z-index is managed by the DialogManager and may be overwritten at any time
31155     setZIndex : function(index){
31156         if(this.modal){
31157             this.mask.setStyle("z-index", index);
31158         }
31159         if(this.shim){
31160             this.shim.setStyle("z-index", ++index);
31161         }
31162         if(this.shadow){
31163             this.shadow.setZIndex(++index);
31164         }
31165         this.el.setStyle("z-index", ++index);
31166         if(this.proxy){
31167             this.proxy.setStyle("z-index", ++index);
31168         }
31169         if(this.resizer){
31170             this.resizer.proxy.setStyle("z-index", ++index);
31171         }
31172
31173         this.lastZIndex = index;
31174     },
31175
31176     /**
31177      * Returns the element for this dialog
31178      * @return {Roo.Element} The underlying dialog Element
31179      */
31180     getEl : function(){
31181         return this.el;
31182     }
31183 });
31184
31185 /**
31186  * @class Roo.DialogManager
31187  * Provides global access to BasicDialogs that have been created and
31188  * support for z-indexing (layering) multiple open dialogs.
31189  */
31190 Roo.DialogManager = function(){
31191     var list = {};
31192     var accessList = [];
31193     var front = null;
31194
31195     // private
31196     var sortDialogs = function(d1, d2){
31197         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31198     };
31199
31200     // private
31201     var orderDialogs = function(){
31202         accessList.sort(sortDialogs);
31203         var seed = Roo.DialogManager.zseed;
31204         for(var i = 0, len = accessList.length; i < len; i++){
31205             var dlg = accessList[i];
31206             if(dlg){
31207                 dlg.setZIndex(seed + (i*10));
31208             }
31209         }
31210     };
31211
31212     return {
31213         /**
31214          * The starting z-index for BasicDialogs (defaults to 9000)
31215          * @type Number The z-index value
31216          */
31217         zseed : 9000,
31218
31219         // private
31220         register : function(dlg){
31221             list[dlg.id] = dlg;
31222             accessList.push(dlg);
31223         },
31224
31225         // private
31226         unregister : function(dlg){
31227             delete list[dlg.id];
31228             var i=0;
31229             var len=0;
31230             if(!accessList.indexOf){
31231                 for(  i = 0, len = accessList.length; i < len; i++){
31232                     if(accessList[i] == dlg){
31233                         accessList.splice(i, 1);
31234                         return;
31235                     }
31236                 }
31237             }else{
31238                  i = accessList.indexOf(dlg);
31239                 if(i != -1){
31240                     accessList.splice(i, 1);
31241                 }
31242             }
31243         },
31244
31245         /**
31246          * Gets a registered dialog by id
31247          * @param {String/Object} id The id of the dialog or a dialog
31248          * @return {Roo.BasicDialog} this
31249          */
31250         get : function(id){
31251             return typeof id == "object" ? id : list[id];
31252         },
31253
31254         /**
31255          * Brings the specified dialog to the front
31256          * @param {String/Object} dlg The id of the dialog or a dialog
31257          * @return {Roo.BasicDialog} this
31258          */
31259         bringToFront : function(dlg){
31260             dlg = this.get(dlg);
31261             if(dlg != front){
31262                 front = dlg;
31263                 dlg._lastAccess = new Date().getTime();
31264                 orderDialogs();
31265             }
31266             return dlg;
31267         },
31268
31269         /**
31270          * Sends the specified dialog to the back
31271          * @param {String/Object} dlg The id of the dialog or a dialog
31272          * @return {Roo.BasicDialog} this
31273          */
31274         sendToBack : function(dlg){
31275             dlg = this.get(dlg);
31276             dlg._lastAccess = -(new Date().getTime());
31277             orderDialogs();
31278             return dlg;
31279         },
31280
31281         /**
31282          * Hides all dialogs
31283          */
31284         hideAll : function(){
31285             for(var id in list){
31286                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31287                     list[id].hide();
31288                 }
31289             }
31290         }
31291     };
31292 }();
31293
31294 /**
31295  * @class Roo.LayoutDialog
31296  * @extends Roo.BasicDialog
31297  * Dialog which provides adjustments for working with a layout in a Dialog.
31298  * Add your necessary layout config options to the dialog's config.<br>
31299  * Example usage (including a nested layout):
31300  * <pre><code>
31301 if(!dialog){
31302     dialog = new Roo.LayoutDialog("download-dlg", {
31303         modal: true,
31304         width:600,
31305         height:450,
31306         shadow:true,
31307         minWidth:500,
31308         minHeight:350,
31309         autoTabs:true,
31310         proxyDrag:true,
31311         // layout config merges with the dialog config
31312         center:{
31313             tabPosition: "top",
31314             alwaysShowTabs: true
31315         }
31316     });
31317     dialog.addKeyListener(27, dialog.hide, dialog);
31318     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31319     dialog.addButton("Build It!", this.getDownload, this);
31320
31321     // we can even add nested layouts
31322     var innerLayout = new Roo.BorderLayout("dl-inner", {
31323         east: {
31324             initialSize: 200,
31325             autoScroll:true,
31326             split:true
31327         },
31328         center: {
31329             autoScroll:true
31330         }
31331     });
31332     innerLayout.beginUpdate();
31333     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31334     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31335     innerLayout.endUpdate(true);
31336
31337     var layout = dialog.getLayout();
31338     layout.beginUpdate();
31339     layout.add("center", new Roo.ContentPanel("standard-panel",
31340                         {title: "Download the Source", fitToFrame:true}));
31341     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31342                {title: "Build your own roo.js"}));
31343     layout.getRegion("center").showPanel(sp);
31344     layout.endUpdate();
31345 }
31346 </code></pre>
31347     * @constructor
31348     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31349     * @param {Object} config configuration options
31350   */
31351 Roo.LayoutDialog = function(el, cfg){
31352     
31353     var config=  cfg;
31354     if (typeof(cfg) == 'undefined') {
31355         config = Roo.apply({}, el);
31356         // not sure why we use documentElement here.. - it should always be body.
31357         // IE7 borks horribly if we use documentElement.
31358         // webkit also does not like documentElement - it creates a body element...
31359         el = Roo.get( document.body || document.documentElement ).createChild();
31360         //config.autoCreate = true;
31361     }
31362     
31363     
31364     config.autoTabs = false;
31365     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31366     this.body.setStyle({overflow:"hidden", position:"relative"});
31367     this.layout = new Roo.BorderLayout(this.body.dom, config);
31368     this.layout.monitorWindowResize = false;
31369     this.el.addClass("x-dlg-auto-layout");
31370     // fix case when center region overwrites center function
31371     this.center = Roo.BasicDialog.prototype.center;
31372     this.on("show", this.layout.layout, this.layout, true);
31373     if (config.items) {
31374         var xitems = config.items;
31375         delete config.items;
31376         Roo.each(xitems, this.addxtype, this);
31377     }
31378     
31379     
31380 };
31381 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31382     /**
31383      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31384      * @deprecated
31385      */
31386     endUpdate : function(){
31387         this.layout.endUpdate();
31388     },
31389
31390     /**
31391      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31392      *  @deprecated
31393      */
31394     beginUpdate : function(){
31395         this.layout.beginUpdate();
31396     },
31397
31398     /**
31399      * Get the BorderLayout for this dialog
31400      * @return {Roo.BorderLayout}
31401      */
31402     getLayout : function(){
31403         return this.layout;
31404     },
31405
31406     showEl : function(){
31407         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31408         if(Roo.isIE7){
31409             this.layout.layout();
31410         }
31411     },
31412
31413     // private
31414     // Use the syncHeightBeforeShow config option to control this automatically
31415     syncBodyHeight : function(){
31416         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31417         if(this.layout){this.layout.layout();}
31418     },
31419     
31420       /**
31421      * Add an xtype element (actually adds to the layout.)
31422      * @return {Object} xdata xtype object data.
31423      */
31424     
31425     addxtype : function(c) {
31426         return this.layout.addxtype(c);
31427     }
31428 });/*
31429  * Based on:
31430  * Ext JS Library 1.1.1
31431  * Copyright(c) 2006-2007, Ext JS, LLC.
31432  *
31433  * Originally Released Under LGPL - original licence link has changed is not relivant.
31434  *
31435  * Fork - LGPL
31436  * <script type="text/javascript">
31437  */
31438  
31439 /**
31440  * @class Roo.MessageBox
31441  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31442  * Example usage:
31443  *<pre><code>
31444 // Basic alert:
31445 Roo.Msg.alert('Status', 'Changes saved successfully.');
31446
31447 // Prompt for user data:
31448 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31449     if (btn == 'ok'){
31450         // process text value...
31451     }
31452 });
31453
31454 // Show a dialog using config options:
31455 Roo.Msg.show({
31456    title:'Save Changes?',
31457    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31458    buttons: Roo.Msg.YESNOCANCEL,
31459    fn: processResult,
31460    animEl: 'elId'
31461 });
31462 </code></pre>
31463  * @singleton
31464  */
31465 Roo.MessageBox = function(){
31466     var dlg, opt, mask, waitTimer;
31467     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31468     var buttons, activeTextEl, bwidth;
31469
31470     // private
31471     var handleButton = function(button){
31472         dlg.hide();
31473         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31474     };
31475
31476     // private
31477     var handleHide = function(){
31478         if(opt && opt.cls){
31479             dlg.el.removeClass(opt.cls);
31480         }
31481         if(waitTimer){
31482             Roo.TaskMgr.stop(waitTimer);
31483             waitTimer = null;
31484         }
31485     };
31486
31487     // private
31488     var updateButtons = function(b){
31489         var width = 0;
31490         if(!b){
31491             buttons["ok"].hide();
31492             buttons["cancel"].hide();
31493             buttons["yes"].hide();
31494             buttons["no"].hide();
31495             dlg.footer.dom.style.display = 'none';
31496             return width;
31497         }
31498         dlg.footer.dom.style.display = '';
31499         for(var k in buttons){
31500             if(typeof buttons[k] != "function"){
31501                 if(b[k]){
31502                     buttons[k].show();
31503                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31504                     width += buttons[k].el.getWidth()+15;
31505                 }else{
31506                     buttons[k].hide();
31507                 }
31508             }
31509         }
31510         return width;
31511     };
31512
31513     // private
31514     var handleEsc = function(d, k, e){
31515         if(opt && opt.closable !== false){
31516             dlg.hide();
31517         }
31518         if(e){
31519             e.stopEvent();
31520         }
31521     };
31522
31523     return {
31524         /**
31525          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31526          * @return {Roo.BasicDialog} The BasicDialog element
31527          */
31528         getDialog : function(){
31529            if(!dlg){
31530                 dlg = new Roo.BasicDialog("x-msg-box", {
31531                     autoCreate : true,
31532                     shadow: true,
31533                     draggable: true,
31534                     resizable:false,
31535                     constraintoviewport:false,
31536                     fixedcenter:true,
31537                     collapsible : false,
31538                     shim:true,
31539                     modal: true,
31540                     width:400, height:100,
31541                     buttonAlign:"center",
31542                     closeClick : function(){
31543                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31544                             handleButton("no");
31545                         }else{
31546                             handleButton("cancel");
31547                         }
31548                     }
31549                 });
31550                 dlg.on("hide", handleHide);
31551                 mask = dlg.mask;
31552                 dlg.addKeyListener(27, handleEsc);
31553                 buttons = {};
31554                 var bt = this.buttonText;
31555                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31556                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31557                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31558                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31559                 bodyEl = dlg.body.createChild({
31560
31561                     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>'
31562                 });
31563                 msgEl = bodyEl.dom.firstChild;
31564                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31565                 textboxEl.enableDisplayMode();
31566                 textboxEl.addKeyListener([10,13], function(){
31567                     if(dlg.isVisible() && opt && opt.buttons){
31568                         if(opt.buttons.ok){
31569                             handleButton("ok");
31570                         }else if(opt.buttons.yes){
31571                             handleButton("yes");
31572                         }
31573                     }
31574                 });
31575                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31576                 textareaEl.enableDisplayMode();
31577                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31578                 progressEl.enableDisplayMode();
31579                 var pf = progressEl.dom.firstChild;
31580                 if (pf) {
31581                     pp = Roo.get(pf.firstChild);
31582                     pp.setHeight(pf.offsetHeight);
31583                 }
31584                 
31585             }
31586             return dlg;
31587         },
31588
31589         /**
31590          * Updates the message box body text
31591          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31592          * the XHTML-compliant non-breaking space character '&amp;#160;')
31593          * @return {Roo.MessageBox} This message box
31594          */
31595         updateText : function(text){
31596             if(!dlg.isVisible() && !opt.width){
31597                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31598             }
31599             msgEl.innerHTML = text || '&#160;';
31600       
31601             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31602             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31603             var w = Math.max(
31604                     Math.min(opt.width || cw , this.maxWidth), 
31605                     Math.max(opt.minWidth || this.minWidth, bwidth)
31606             );
31607             if(opt.prompt){
31608                 activeTextEl.setWidth(w);
31609             }
31610             if(dlg.isVisible()){
31611                 dlg.fixedcenter = false;
31612             }
31613             // to big, make it scroll. = But as usual stupid IE does not support
31614             // !important..
31615             
31616             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31617                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31618                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31619             } else {
31620                 bodyEl.dom.style.height = '';
31621                 bodyEl.dom.style.overflowY = '';
31622             }
31623             if (cw > w) {
31624                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31625             } else {
31626                 bodyEl.dom.style.overflowX = '';
31627             }
31628             
31629             dlg.setContentSize(w, bodyEl.getHeight());
31630             if(dlg.isVisible()){
31631                 dlg.fixedcenter = true;
31632             }
31633             return this;
31634         },
31635
31636         /**
31637          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31638          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31639          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31640          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31641          * @return {Roo.MessageBox} This message box
31642          */
31643         updateProgress : function(value, text){
31644             if(text){
31645                 this.updateText(text);
31646             }
31647             if (pp) { // weird bug on my firefox - for some reason this is not defined
31648                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31649             }
31650             return this;
31651         },        
31652
31653         /**
31654          * Returns true if the message box is currently displayed
31655          * @return {Boolean} True if the message box is visible, else false
31656          */
31657         isVisible : function(){
31658             return dlg && dlg.isVisible();  
31659         },
31660
31661         /**
31662          * Hides the message box if it is displayed
31663          */
31664         hide : function(){
31665             if(this.isVisible()){
31666                 dlg.hide();
31667             }  
31668         },
31669
31670         /**
31671          * Displays a new message box, or reinitializes an existing message box, based on the config options
31672          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31673          * The following config object properties are supported:
31674          * <pre>
31675 Property    Type             Description
31676 ----------  ---------------  ------------------------------------------------------------------------------------
31677 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31678                                    closes (defaults to undefined)
31679 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31680                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31681 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31682                                    progress and wait dialogs will ignore this property and always hide the
31683                                    close button as they can only be closed programmatically.
31684 cls               String           A custom CSS class to apply to the message box element
31685 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31686                                    displayed (defaults to 75)
31687 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31688                                    function will be btn (the name of the button that was clicked, if applicable,
31689                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31690                                    Progress and wait dialogs will ignore this option since they do not respond to
31691                                    user actions and can only be closed programmatically, so any required function
31692                                    should be called by the same code after it closes the dialog.
31693 icon              String           A CSS class that provides a background image to be used as an icon for
31694                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31695 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31696 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31697 modal             Boolean          False to allow user interaction with the page while the message box is
31698                                    displayed (defaults to true)
31699 msg               String           A string that will replace the existing message box body text (defaults
31700                                    to the XHTML-compliant non-breaking space character '&#160;')
31701 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31702 progress          Boolean          True to display a progress bar (defaults to false)
31703 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31704 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31705 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31706 title             String           The title text
31707 value             String           The string value to set into the active textbox element if displayed
31708 wait              Boolean          True to display a progress bar (defaults to false)
31709 width             Number           The width of the dialog in pixels
31710 </pre>
31711          *
31712          * Example usage:
31713          * <pre><code>
31714 Roo.Msg.show({
31715    title: 'Address',
31716    msg: 'Please enter your address:',
31717    width: 300,
31718    buttons: Roo.MessageBox.OKCANCEL,
31719    multiline: true,
31720    fn: saveAddress,
31721    animEl: 'addAddressBtn'
31722 });
31723 </code></pre>
31724          * @param {Object} config Configuration options
31725          * @return {Roo.MessageBox} This message box
31726          */
31727         show : function(options)
31728         {
31729             
31730             // this causes nightmares if you show one dialog after another
31731             // especially on callbacks..
31732              
31733             if(this.isVisible()){
31734                 
31735                 this.hide();
31736                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31737                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31738                 Roo.log("New Dialog Message:" +  options.msg )
31739                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31740                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31741                 
31742             }
31743             var d = this.getDialog();
31744             opt = options;
31745             d.setTitle(opt.title || "&#160;");
31746             d.close.setDisplayed(opt.closable !== false);
31747             activeTextEl = textboxEl;
31748             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31749             if(opt.prompt){
31750                 if(opt.multiline){
31751                     textboxEl.hide();
31752                     textareaEl.show();
31753                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31754                         opt.multiline : this.defaultTextHeight);
31755                     activeTextEl = textareaEl;
31756                 }else{
31757                     textboxEl.show();
31758                     textareaEl.hide();
31759                 }
31760             }else{
31761                 textboxEl.hide();
31762                 textareaEl.hide();
31763             }
31764             progressEl.setDisplayed(opt.progress === true);
31765             this.updateProgress(0);
31766             activeTextEl.dom.value = opt.value || "";
31767             if(opt.prompt){
31768                 dlg.setDefaultButton(activeTextEl);
31769             }else{
31770                 var bs = opt.buttons;
31771                 var db = null;
31772                 if(bs && bs.ok){
31773                     db = buttons["ok"];
31774                 }else if(bs && bs.yes){
31775                     db = buttons["yes"];
31776                 }
31777                 dlg.setDefaultButton(db);
31778             }
31779             bwidth = updateButtons(opt.buttons);
31780             this.updateText(opt.msg);
31781             if(opt.cls){
31782                 d.el.addClass(opt.cls);
31783             }
31784             d.proxyDrag = opt.proxyDrag === true;
31785             d.modal = opt.modal !== false;
31786             d.mask = opt.modal !== false ? mask : false;
31787             if(!d.isVisible()){
31788                 // force it to the end of the z-index stack so it gets a cursor in FF
31789                 document.body.appendChild(dlg.el.dom);
31790                 d.animateTarget = null;
31791                 d.show(options.animEl);
31792             }
31793             return this;
31794         },
31795
31796         /**
31797          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31798          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31799          * and closing the message box when the process is complete.
31800          * @param {String} title The title bar text
31801          * @param {String} msg The message box body text
31802          * @return {Roo.MessageBox} This message box
31803          */
31804         progress : function(title, msg){
31805             this.show({
31806                 title : title,
31807                 msg : msg,
31808                 buttons: false,
31809                 progress:true,
31810                 closable:false,
31811                 minWidth: this.minProgressWidth,
31812                 modal : true
31813             });
31814             return this;
31815         },
31816
31817         /**
31818          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31819          * If a callback function is passed it will be called after the user clicks the button, and the
31820          * id of the button that was clicked will be passed as the only parameter to the callback
31821          * (could also be the top-right close button).
31822          * @param {String} title The title bar text
31823          * @param {String} msg The message box body text
31824          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31825          * @param {Object} scope (optional) The scope of the callback function
31826          * @return {Roo.MessageBox} This message box
31827          */
31828         alert : function(title, msg, fn, scope){
31829             this.show({
31830                 title : title,
31831                 msg : msg,
31832                 buttons: this.OK,
31833                 fn: fn,
31834                 scope : scope,
31835                 modal : true
31836             });
31837             return this;
31838         },
31839
31840         /**
31841          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31842          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31843          * You are responsible for closing the message box when the process is complete.
31844          * @param {String} msg The message box body text
31845          * @param {String} title (optional) The title bar text
31846          * @return {Roo.MessageBox} This message box
31847          */
31848         wait : function(msg, title){
31849             this.show({
31850                 title : title,
31851                 msg : msg,
31852                 buttons: false,
31853                 closable:false,
31854                 progress:true,
31855                 modal:true,
31856                 width:300,
31857                 wait:true
31858             });
31859             waitTimer = Roo.TaskMgr.start({
31860                 run: function(i){
31861                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31862                 },
31863                 interval: 1000
31864             });
31865             return this;
31866         },
31867
31868         /**
31869          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31870          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31871          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31872          * @param {String} title The title bar text
31873          * @param {String} msg The message box body text
31874          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31875          * @param {Object} scope (optional) The scope of the callback function
31876          * @return {Roo.MessageBox} This message box
31877          */
31878         confirm : function(title, msg, fn, scope){
31879             this.show({
31880                 title : title,
31881                 msg : msg,
31882                 buttons: this.YESNO,
31883                 fn: fn,
31884                 scope : scope,
31885                 modal : true
31886             });
31887             return this;
31888         },
31889
31890         /**
31891          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31892          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31893          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31894          * (could also be the top-right close button) and the text that was entered will be passed as the two
31895          * parameters to the callback.
31896          * @param {String} title The title bar text
31897          * @param {String} msg The message box body text
31898          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31899          * @param {Object} scope (optional) The scope of the callback function
31900          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31901          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31902          * @return {Roo.MessageBox} This message box
31903          */
31904         prompt : function(title, msg, fn, scope, multiline){
31905             this.show({
31906                 title : title,
31907                 msg : msg,
31908                 buttons: this.OKCANCEL,
31909                 fn: fn,
31910                 minWidth:250,
31911                 scope : scope,
31912                 prompt:true,
31913                 multiline: multiline,
31914                 modal : true
31915             });
31916             return this;
31917         },
31918
31919         /**
31920          * Button config that displays a single OK button
31921          * @type Object
31922          */
31923         OK : {ok:true},
31924         /**
31925          * Button config that displays Yes and No buttons
31926          * @type Object
31927          */
31928         YESNO : {yes:true, no:true},
31929         /**
31930          * Button config that displays OK and Cancel buttons
31931          * @type Object
31932          */
31933         OKCANCEL : {ok:true, cancel:true},
31934         /**
31935          * Button config that displays Yes, No and Cancel buttons
31936          * @type Object
31937          */
31938         YESNOCANCEL : {yes:true, no:true, cancel:true},
31939
31940         /**
31941          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31942          * @type Number
31943          */
31944         defaultTextHeight : 75,
31945         /**
31946          * The maximum width in pixels of the message box (defaults to 600)
31947          * @type Number
31948          */
31949         maxWidth : 600,
31950         /**
31951          * The minimum width in pixels of the message box (defaults to 100)
31952          * @type Number
31953          */
31954         minWidth : 100,
31955         /**
31956          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31957          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31958          * @type Number
31959          */
31960         minProgressWidth : 250,
31961         /**
31962          * An object containing the default button text strings that can be overriden for localized language support.
31963          * Supported properties are: ok, cancel, yes and no.
31964          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31965          * @type Object
31966          */
31967         buttonText : {
31968             ok : "OK",
31969             cancel : "Cancel",
31970             yes : "Yes",
31971             no : "No"
31972         }
31973     };
31974 }();
31975
31976 /**
31977  * Shorthand for {@link Roo.MessageBox}
31978  */
31979 Roo.Msg = Roo.MessageBox;/*
31980  * Based on:
31981  * Ext JS Library 1.1.1
31982  * Copyright(c) 2006-2007, Ext JS, LLC.
31983  *
31984  * Originally Released Under LGPL - original licence link has changed is not relivant.
31985  *
31986  * Fork - LGPL
31987  * <script type="text/javascript">
31988  */
31989 /**
31990  * @class Roo.QuickTips
31991  * Provides attractive and customizable tooltips for any element.
31992  * @singleton
31993  */
31994 Roo.QuickTips = function(){
31995     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31996     var ce, bd, xy, dd;
31997     var visible = false, disabled = true, inited = false;
31998     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31999     
32000     var onOver = function(e){
32001         if(disabled){
32002             return;
32003         }
32004         var t = e.getTarget();
32005         if(!t || t.nodeType !== 1 || t == document || t == document.body){
32006             return;
32007         }
32008         if(ce && t == ce.el){
32009             clearTimeout(hideProc);
32010             return;
32011         }
32012         if(t && tagEls[t.id]){
32013             tagEls[t.id].el = t;
32014             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32015             return;
32016         }
32017         var ttp, et = Roo.fly(t);
32018         var ns = cfg.namespace;
32019         if(tm.interceptTitles && t.title){
32020             ttp = t.title;
32021             t.qtip = ttp;
32022             t.removeAttribute("title");
32023             e.preventDefault();
32024         }else{
32025             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32026         }
32027         if(ttp){
32028             showProc = show.defer(tm.showDelay, tm, [{
32029                 el: t, 
32030                 text: ttp, 
32031                 width: et.getAttributeNS(ns, cfg.width),
32032                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32033                 title: et.getAttributeNS(ns, cfg.title),
32034                     cls: et.getAttributeNS(ns, cfg.cls)
32035             }]);
32036         }
32037     };
32038     
32039     var onOut = function(e){
32040         clearTimeout(showProc);
32041         var t = e.getTarget();
32042         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32043             hideProc = setTimeout(hide, tm.hideDelay);
32044         }
32045     };
32046     
32047     var onMove = function(e){
32048         if(disabled){
32049             return;
32050         }
32051         xy = e.getXY();
32052         xy[1] += 18;
32053         if(tm.trackMouse && ce){
32054             el.setXY(xy);
32055         }
32056     };
32057     
32058     var onDown = function(e){
32059         clearTimeout(showProc);
32060         clearTimeout(hideProc);
32061         if(!e.within(el)){
32062             if(tm.hideOnClick){
32063                 hide();
32064                 tm.disable();
32065                 tm.enable.defer(100, tm);
32066             }
32067         }
32068     };
32069     
32070     var getPad = function(){
32071         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32072     };
32073
32074     var show = function(o){
32075         if(disabled){
32076             return;
32077         }
32078         clearTimeout(dismissProc);
32079         ce = o;
32080         if(removeCls){ // in case manually hidden
32081             el.removeClass(removeCls);
32082             removeCls = null;
32083         }
32084         if(ce.cls){
32085             el.addClass(ce.cls);
32086             removeCls = ce.cls;
32087         }
32088         if(ce.title){
32089             tipTitle.update(ce.title);
32090             tipTitle.show();
32091         }else{
32092             tipTitle.update('');
32093             tipTitle.hide();
32094         }
32095         el.dom.style.width  = tm.maxWidth+'px';
32096         //tipBody.dom.style.width = '';
32097         tipBodyText.update(o.text);
32098         var p = getPad(), w = ce.width;
32099         if(!w){
32100             var td = tipBodyText.dom;
32101             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32102             if(aw > tm.maxWidth){
32103                 w = tm.maxWidth;
32104             }else if(aw < tm.minWidth){
32105                 w = tm.minWidth;
32106             }else{
32107                 w = aw;
32108             }
32109         }
32110         //tipBody.setWidth(w);
32111         el.setWidth(parseInt(w, 10) + p);
32112         if(ce.autoHide === false){
32113             close.setDisplayed(true);
32114             if(dd){
32115                 dd.unlock();
32116             }
32117         }else{
32118             close.setDisplayed(false);
32119             if(dd){
32120                 dd.lock();
32121             }
32122         }
32123         if(xy){
32124             el.avoidY = xy[1]-18;
32125             el.setXY(xy);
32126         }
32127         if(tm.animate){
32128             el.setOpacity(.1);
32129             el.setStyle("visibility", "visible");
32130             el.fadeIn({callback: afterShow});
32131         }else{
32132             afterShow();
32133         }
32134     };
32135     
32136     var afterShow = function(){
32137         if(ce){
32138             el.show();
32139             esc.enable();
32140             if(tm.autoDismiss && ce.autoHide !== false){
32141                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32142             }
32143         }
32144     };
32145     
32146     var hide = function(noanim){
32147         clearTimeout(dismissProc);
32148         clearTimeout(hideProc);
32149         ce = null;
32150         if(el.isVisible()){
32151             esc.disable();
32152             if(noanim !== true && tm.animate){
32153                 el.fadeOut({callback: afterHide});
32154             }else{
32155                 afterHide();
32156             } 
32157         }
32158     };
32159     
32160     var afterHide = function(){
32161         el.hide();
32162         if(removeCls){
32163             el.removeClass(removeCls);
32164             removeCls = null;
32165         }
32166     };
32167     
32168     return {
32169         /**
32170         * @cfg {Number} minWidth
32171         * The minimum width of the quick tip (defaults to 40)
32172         */
32173        minWidth : 40,
32174         /**
32175         * @cfg {Number} maxWidth
32176         * The maximum width of the quick tip (defaults to 300)
32177         */
32178        maxWidth : 300,
32179         /**
32180         * @cfg {Boolean} interceptTitles
32181         * True to automatically use the element's DOM title value if available (defaults to false)
32182         */
32183        interceptTitles : false,
32184         /**
32185         * @cfg {Boolean} trackMouse
32186         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32187         */
32188        trackMouse : false,
32189         /**
32190         * @cfg {Boolean} hideOnClick
32191         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32192         */
32193        hideOnClick : true,
32194         /**
32195         * @cfg {Number} showDelay
32196         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32197         */
32198        showDelay : 500,
32199         /**
32200         * @cfg {Number} hideDelay
32201         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32202         */
32203        hideDelay : 200,
32204         /**
32205         * @cfg {Boolean} autoHide
32206         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32207         * Used in conjunction with hideDelay.
32208         */
32209        autoHide : true,
32210         /**
32211         * @cfg {Boolean}
32212         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32213         * (defaults to true).  Used in conjunction with autoDismissDelay.
32214         */
32215        autoDismiss : true,
32216         /**
32217         * @cfg {Number}
32218         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32219         */
32220        autoDismissDelay : 5000,
32221        /**
32222         * @cfg {Boolean} animate
32223         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32224         */
32225        animate : false,
32226
32227        /**
32228         * @cfg {String} title
32229         * Title text to display (defaults to '').  This can be any valid HTML markup.
32230         */
32231         title: '',
32232        /**
32233         * @cfg {String} text
32234         * Body text to display (defaults to '').  This can be any valid HTML markup.
32235         */
32236         text : '',
32237        /**
32238         * @cfg {String} cls
32239         * A CSS class to apply to the base quick tip element (defaults to '').
32240         */
32241         cls : '',
32242        /**
32243         * @cfg {Number} width
32244         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32245         * minWidth or maxWidth.
32246         */
32247         width : null,
32248
32249     /**
32250      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32251      * or display QuickTips in a page.
32252      */
32253        init : function(){
32254           tm = Roo.QuickTips;
32255           cfg = tm.tagConfig;
32256           if(!inited){
32257               if(!Roo.isReady){ // allow calling of init() before onReady
32258                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32259                   return;
32260               }
32261               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32262               el.fxDefaults = {stopFx: true};
32263               // maximum custom styling
32264               //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>');
32265               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>');              
32266               tipTitle = el.child('h3');
32267               tipTitle.enableDisplayMode("block");
32268               tipBody = el.child('div.x-tip-bd');
32269               tipBodyText = el.child('div.x-tip-bd-inner');
32270               //bdLeft = el.child('div.x-tip-bd-left');
32271               //bdRight = el.child('div.x-tip-bd-right');
32272               close = el.child('div.x-tip-close');
32273               close.enableDisplayMode("block");
32274               close.on("click", hide);
32275               var d = Roo.get(document);
32276               d.on("mousedown", onDown);
32277               d.on("mouseover", onOver);
32278               d.on("mouseout", onOut);
32279               d.on("mousemove", onMove);
32280               esc = d.addKeyListener(27, hide);
32281               esc.disable();
32282               if(Roo.dd.DD){
32283                   dd = el.initDD("default", null, {
32284                       onDrag : function(){
32285                           el.sync();  
32286                       }
32287                   });
32288                   dd.setHandleElId(tipTitle.id);
32289                   dd.lock();
32290               }
32291               inited = true;
32292           }
32293           this.enable(); 
32294        },
32295
32296     /**
32297      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32298      * are supported:
32299      * <pre>
32300 Property    Type                   Description
32301 ----------  ---------------------  ------------------------------------------------------------------------
32302 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32303      * </ul>
32304      * @param {Object} config The config object
32305      */
32306        register : function(config){
32307            var cs = config instanceof Array ? config : arguments;
32308            for(var i = 0, len = cs.length; i < len; i++) {
32309                var c = cs[i];
32310                var target = c.target;
32311                if(target){
32312                    if(target instanceof Array){
32313                        for(var j = 0, jlen = target.length; j < jlen; j++){
32314                            tagEls[target[j]] = c;
32315                        }
32316                    }else{
32317                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32318                    }
32319                }
32320            }
32321        },
32322
32323     /**
32324      * Removes this quick tip from its element and destroys it.
32325      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32326      */
32327        unregister : function(el){
32328            delete tagEls[Roo.id(el)];
32329        },
32330
32331     /**
32332      * Enable this quick tip.
32333      */
32334        enable : function(){
32335            if(inited && disabled){
32336                locks.pop();
32337                if(locks.length < 1){
32338                    disabled = false;
32339                }
32340            }
32341        },
32342
32343     /**
32344      * Disable this quick tip.
32345      */
32346        disable : function(){
32347           disabled = true;
32348           clearTimeout(showProc);
32349           clearTimeout(hideProc);
32350           clearTimeout(dismissProc);
32351           if(ce){
32352               hide(true);
32353           }
32354           locks.push(1);
32355        },
32356
32357     /**
32358      * Returns true if the quick tip is enabled, else false.
32359      */
32360        isEnabled : function(){
32361             return !disabled;
32362        },
32363
32364         // private
32365        tagConfig : {
32366            namespace : "ext",
32367            attribute : "qtip",
32368            width : "width",
32369            target : "target",
32370            title : "qtitle",
32371            hide : "hide",
32372            cls : "qclass"
32373        }
32374    };
32375 }();
32376
32377 // backwards compat
32378 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32379  * Based on:
32380  * Ext JS Library 1.1.1
32381  * Copyright(c) 2006-2007, Ext JS, LLC.
32382  *
32383  * Originally Released Under LGPL - original licence link has changed is not relivant.
32384  *
32385  * Fork - LGPL
32386  * <script type="text/javascript">
32387  */
32388  
32389
32390 /**
32391  * @class Roo.tree.TreePanel
32392  * @extends Roo.data.Tree
32393
32394  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32395  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32396  * @cfg {Boolean} enableDD true to enable drag and drop
32397  * @cfg {Boolean} enableDrag true to enable just drag
32398  * @cfg {Boolean} enableDrop true to enable just drop
32399  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32400  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32401  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32402  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32403  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32404  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32405  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32406  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32407  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32408  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32409  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32410  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32411  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32412  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32413  * @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>
32414  * @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>
32415  * 
32416  * @constructor
32417  * @param {String/HTMLElement/Element} el The container element
32418  * @param {Object} config
32419  */
32420 Roo.tree.TreePanel = function(el, config){
32421     var root = false;
32422     var loader = false;
32423     if (config.root) {
32424         root = config.root;
32425         delete config.root;
32426     }
32427     if (config.loader) {
32428         loader = config.loader;
32429         delete config.loader;
32430     }
32431     
32432     Roo.apply(this, config);
32433     Roo.tree.TreePanel.superclass.constructor.call(this);
32434     this.el = Roo.get(el);
32435     this.el.addClass('x-tree');
32436     //console.log(root);
32437     if (root) {
32438         this.setRootNode( Roo.factory(root, Roo.tree));
32439     }
32440     if (loader) {
32441         this.loader = Roo.factory(loader, Roo.tree);
32442     }
32443    /**
32444     * Read-only. The id of the container element becomes this TreePanel's id.
32445     */
32446     this.id = this.el.id;
32447     this.addEvents({
32448         /**
32449         * @event beforeload
32450         * Fires before a node is loaded, return false to cancel
32451         * @param {Node} node The node being loaded
32452         */
32453         "beforeload" : true,
32454         /**
32455         * @event load
32456         * Fires when a node is loaded
32457         * @param {Node} node The node that was loaded
32458         */
32459         "load" : true,
32460         /**
32461         * @event textchange
32462         * Fires when the text for a node is changed
32463         * @param {Node} node The node
32464         * @param {String} text The new text
32465         * @param {String} oldText The old text
32466         */
32467         "textchange" : true,
32468         /**
32469         * @event beforeexpand
32470         * Fires before a node is expanded, return false to cancel.
32471         * @param {Node} node The node
32472         * @param {Boolean} deep
32473         * @param {Boolean} anim
32474         */
32475         "beforeexpand" : true,
32476         /**
32477         * @event beforecollapse
32478         * Fires before a node is collapsed, return false to cancel.
32479         * @param {Node} node The node
32480         * @param {Boolean} deep
32481         * @param {Boolean} anim
32482         */
32483         "beforecollapse" : true,
32484         /**
32485         * @event expand
32486         * Fires when a node is expanded
32487         * @param {Node} node The node
32488         */
32489         "expand" : true,
32490         /**
32491         * @event disabledchange
32492         * Fires when the disabled status of a node changes
32493         * @param {Node} node The node
32494         * @param {Boolean} disabled
32495         */
32496         "disabledchange" : true,
32497         /**
32498         * @event collapse
32499         * Fires when a node is collapsed
32500         * @param {Node} node The node
32501         */
32502         "collapse" : true,
32503         /**
32504         * @event beforeclick
32505         * Fires before click processing on a node. Return false to cancel the default action.
32506         * @param {Node} node The node
32507         * @param {Roo.EventObject} e The event object
32508         */
32509         "beforeclick":true,
32510         /**
32511         * @event checkchange
32512         * Fires when a node with a checkbox's checked property changes
32513         * @param {Node} this This node
32514         * @param {Boolean} checked
32515         */
32516         "checkchange":true,
32517         /**
32518         * @event click
32519         * Fires when a node is clicked
32520         * @param {Node} node The node
32521         * @param {Roo.EventObject} e The event object
32522         */
32523         "click":true,
32524         /**
32525         * @event dblclick
32526         * Fires when a node is double clicked
32527         * @param {Node} node The node
32528         * @param {Roo.EventObject} e The event object
32529         */
32530         "dblclick":true,
32531         /**
32532         * @event contextmenu
32533         * Fires when a node is right clicked
32534         * @param {Node} node The node
32535         * @param {Roo.EventObject} e The event object
32536         */
32537         "contextmenu":true,
32538         /**
32539         * @event beforechildrenrendered
32540         * Fires right before the child nodes for a node are rendered
32541         * @param {Node} node The node
32542         */
32543         "beforechildrenrendered":true,
32544         /**
32545         * @event startdrag
32546         * Fires when a node starts being dragged
32547         * @param {Roo.tree.TreePanel} this
32548         * @param {Roo.tree.TreeNode} node
32549         * @param {event} e The raw browser event
32550         */ 
32551        "startdrag" : true,
32552        /**
32553         * @event enddrag
32554         * Fires when a drag operation is complete
32555         * @param {Roo.tree.TreePanel} this
32556         * @param {Roo.tree.TreeNode} node
32557         * @param {event} e The raw browser event
32558         */
32559        "enddrag" : true,
32560        /**
32561         * @event dragdrop
32562         * Fires when a dragged node is dropped on a valid DD target
32563         * @param {Roo.tree.TreePanel} this
32564         * @param {Roo.tree.TreeNode} node
32565         * @param {DD} dd The dd it was dropped on
32566         * @param {event} e The raw browser event
32567         */
32568        "dragdrop" : true,
32569        /**
32570         * @event beforenodedrop
32571         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32572         * passed to handlers has the following properties:<br />
32573         * <ul style="padding:5px;padding-left:16px;">
32574         * <li>tree - The TreePanel</li>
32575         * <li>target - The node being targeted for the drop</li>
32576         * <li>data - The drag data from the drag source</li>
32577         * <li>point - The point of the drop - append, above or below</li>
32578         * <li>source - The drag source</li>
32579         * <li>rawEvent - Raw mouse event</li>
32580         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32581         * to be inserted by setting them on this object.</li>
32582         * <li>cancel - Set this to true to cancel the drop.</li>
32583         * </ul>
32584         * @param {Object} dropEvent
32585         */
32586        "beforenodedrop" : true,
32587        /**
32588         * @event nodedrop
32589         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32590         * passed to handlers has the following properties:<br />
32591         * <ul style="padding:5px;padding-left:16px;">
32592         * <li>tree - The TreePanel</li>
32593         * <li>target - The node being targeted for the drop</li>
32594         * <li>data - The drag data from the drag source</li>
32595         * <li>point - The point of the drop - append, above or below</li>
32596         * <li>source - The drag source</li>
32597         * <li>rawEvent - Raw mouse event</li>
32598         * <li>dropNode - Dropped node(s).</li>
32599         * </ul>
32600         * @param {Object} dropEvent
32601         */
32602        "nodedrop" : true,
32603         /**
32604         * @event nodedragover
32605         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32606         * passed to handlers has the following properties:<br />
32607         * <ul style="padding:5px;padding-left:16px;">
32608         * <li>tree - The TreePanel</li>
32609         * <li>target - The node being targeted for the drop</li>
32610         * <li>data - The drag data from the drag source</li>
32611         * <li>point - The point of the drop - append, above or below</li>
32612         * <li>source - The drag source</li>
32613         * <li>rawEvent - Raw mouse event</li>
32614         * <li>dropNode - Drop node(s) provided by the source.</li>
32615         * <li>cancel - Set this to true to signal drop not allowed.</li>
32616         * </ul>
32617         * @param {Object} dragOverEvent
32618         */
32619        "nodedragover" : true
32620         
32621     });
32622     if(this.singleExpand){
32623        this.on("beforeexpand", this.restrictExpand, this);
32624     }
32625     if (this.editor) {
32626         this.editor.tree = this;
32627         this.editor = Roo.factory(this.editor, Roo.tree);
32628     }
32629     
32630     if (this.selModel) {
32631         this.selModel = Roo.factory(this.selModel, Roo.tree);
32632     }
32633    
32634 };
32635 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32636     rootVisible : true,
32637     animate: Roo.enableFx,
32638     lines : true,
32639     enableDD : false,
32640     hlDrop : Roo.enableFx,
32641   
32642     renderer: false,
32643     
32644     rendererTip: false,
32645     // private
32646     restrictExpand : function(node){
32647         var p = node.parentNode;
32648         if(p){
32649             if(p.expandedChild && p.expandedChild.parentNode == p){
32650                 p.expandedChild.collapse();
32651             }
32652             p.expandedChild = node;
32653         }
32654     },
32655
32656     // private override
32657     setRootNode : function(node){
32658         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32659         if(!this.rootVisible){
32660             node.ui = new Roo.tree.RootTreeNodeUI(node);
32661         }
32662         return node;
32663     },
32664
32665     /**
32666      * Returns the container element for this TreePanel
32667      */
32668     getEl : function(){
32669         return this.el;
32670     },
32671
32672     /**
32673      * Returns the default TreeLoader for this TreePanel
32674      */
32675     getLoader : function(){
32676         return this.loader;
32677     },
32678
32679     /**
32680      * Expand all nodes
32681      */
32682     expandAll : function(){
32683         this.root.expand(true);
32684     },
32685
32686     /**
32687      * Collapse all nodes
32688      */
32689     collapseAll : function(){
32690         this.root.collapse(true);
32691     },
32692
32693     /**
32694      * Returns the selection model used by this TreePanel
32695      */
32696     getSelectionModel : function(){
32697         if(!this.selModel){
32698             this.selModel = new Roo.tree.DefaultSelectionModel();
32699         }
32700         return this.selModel;
32701     },
32702
32703     /**
32704      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32705      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32706      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32707      * @return {Array}
32708      */
32709     getChecked : function(a, startNode){
32710         startNode = startNode || this.root;
32711         var r = [];
32712         var f = function(){
32713             if(this.attributes.checked){
32714                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32715             }
32716         }
32717         startNode.cascade(f);
32718         return r;
32719     },
32720
32721     /**
32722      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32723      * @param {String} path
32724      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32725      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32726      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32727      */
32728     expandPath : function(path, attr, callback){
32729         attr = attr || "id";
32730         var keys = path.split(this.pathSeparator);
32731         var curNode = this.root;
32732         if(curNode.attributes[attr] != keys[1]){ // invalid root
32733             if(callback){
32734                 callback(false, null);
32735             }
32736             return;
32737         }
32738         var index = 1;
32739         var f = function(){
32740             if(++index == keys.length){
32741                 if(callback){
32742                     callback(true, curNode);
32743                 }
32744                 return;
32745             }
32746             var c = curNode.findChild(attr, keys[index]);
32747             if(!c){
32748                 if(callback){
32749                     callback(false, curNode);
32750                 }
32751                 return;
32752             }
32753             curNode = c;
32754             c.expand(false, false, f);
32755         };
32756         curNode.expand(false, false, f);
32757     },
32758
32759     /**
32760      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32761      * @param {String} path
32762      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32763      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32764      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32765      */
32766     selectPath : function(path, attr, callback){
32767         attr = attr || "id";
32768         var keys = path.split(this.pathSeparator);
32769         var v = keys.pop();
32770         if(keys.length > 0){
32771             var f = function(success, node){
32772                 if(success && node){
32773                     var n = node.findChild(attr, v);
32774                     if(n){
32775                         n.select();
32776                         if(callback){
32777                             callback(true, n);
32778                         }
32779                     }else if(callback){
32780                         callback(false, n);
32781                     }
32782                 }else{
32783                     if(callback){
32784                         callback(false, n);
32785                     }
32786                 }
32787             };
32788             this.expandPath(keys.join(this.pathSeparator), attr, f);
32789         }else{
32790             this.root.select();
32791             if(callback){
32792                 callback(true, this.root);
32793             }
32794         }
32795     },
32796
32797     getTreeEl : function(){
32798         return this.el;
32799     },
32800
32801     /**
32802      * Trigger rendering of this TreePanel
32803      */
32804     render : function(){
32805         if (this.innerCt) {
32806             return this; // stop it rendering more than once!!
32807         }
32808         
32809         this.innerCt = this.el.createChild({tag:"ul",
32810                cls:"x-tree-root-ct " +
32811                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32812
32813         if(this.containerScroll){
32814             Roo.dd.ScrollManager.register(this.el);
32815         }
32816         if((this.enableDD || this.enableDrop) && !this.dropZone){
32817            /**
32818             * The dropZone used by this tree if drop is enabled
32819             * @type Roo.tree.TreeDropZone
32820             */
32821              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32822                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32823            });
32824         }
32825         if((this.enableDD || this.enableDrag) && !this.dragZone){
32826            /**
32827             * The dragZone used by this tree if drag is enabled
32828             * @type Roo.tree.TreeDragZone
32829             */
32830             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32831                ddGroup: this.ddGroup || "TreeDD",
32832                scroll: this.ddScroll
32833            });
32834         }
32835         this.getSelectionModel().init(this);
32836         if (!this.root) {
32837             Roo.log("ROOT not set in tree");
32838             return this;
32839         }
32840         this.root.render();
32841         if(!this.rootVisible){
32842             this.root.renderChildren();
32843         }
32844         return this;
32845     }
32846 });/*
32847  * Based on:
32848  * Ext JS Library 1.1.1
32849  * Copyright(c) 2006-2007, Ext JS, LLC.
32850  *
32851  * Originally Released Under LGPL - original licence link has changed is not relivant.
32852  *
32853  * Fork - LGPL
32854  * <script type="text/javascript">
32855  */
32856  
32857
32858 /**
32859  * @class Roo.tree.DefaultSelectionModel
32860  * @extends Roo.util.Observable
32861  * The default single selection for a TreePanel.
32862  * @param {Object} cfg Configuration
32863  */
32864 Roo.tree.DefaultSelectionModel = function(cfg){
32865    this.selNode = null;
32866    
32867    
32868    
32869    this.addEvents({
32870        /**
32871         * @event selectionchange
32872         * Fires when the selected node changes
32873         * @param {DefaultSelectionModel} this
32874         * @param {TreeNode} node the new selection
32875         */
32876        "selectionchange" : true,
32877
32878        /**
32879         * @event beforeselect
32880         * Fires before the selected node changes, return false to cancel the change
32881         * @param {DefaultSelectionModel} this
32882         * @param {TreeNode} node the new selection
32883         * @param {TreeNode} node the old selection
32884         */
32885        "beforeselect" : true
32886    });
32887    
32888     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32889 };
32890
32891 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32892     init : function(tree){
32893         this.tree = tree;
32894         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32895         tree.on("click", this.onNodeClick, this);
32896     },
32897     
32898     onNodeClick : function(node, e){
32899         if (e.ctrlKey && this.selNode == node)  {
32900             this.unselect(node);
32901             return;
32902         }
32903         this.select(node);
32904     },
32905     
32906     /**
32907      * Select a node.
32908      * @param {TreeNode} node The node to select
32909      * @return {TreeNode} The selected node
32910      */
32911     select : function(node){
32912         var last = this.selNode;
32913         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32914             if(last){
32915                 last.ui.onSelectedChange(false);
32916             }
32917             this.selNode = node;
32918             node.ui.onSelectedChange(true);
32919             this.fireEvent("selectionchange", this, node, last);
32920         }
32921         return node;
32922     },
32923     
32924     /**
32925      * Deselect a node.
32926      * @param {TreeNode} node The node to unselect
32927      */
32928     unselect : function(node){
32929         if(this.selNode == node){
32930             this.clearSelections();
32931         }    
32932     },
32933     
32934     /**
32935      * Clear all selections
32936      */
32937     clearSelections : function(){
32938         var n = this.selNode;
32939         if(n){
32940             n.ui.onSelectedChange(false);
32941             this.selNode = null;
32942             this.fireEvent("selectionchange", this, null);
32943         }
32944         return n;
32945     },
32946     
32947     /**
32948      * Get the selected node
32949      * @return {TreeNode} The selected node
32950      */
32951     getSelectedNode : function(){
32952         return this.selNode;    
32953     },
32954     
32955     /**
32956      * Returns true if the node is selected
32957      * @param {TreeNode} node The node to check
32958      * @return {Boolean}
32959      */
32960     isSelected : function(node){
32961         return this.selNode == node;  
32962     },
32963
32964     /**
32965      * Selects the node above the selected node in the tree, intelligently walking the nodes
32966      * @return TreeNode The new selection
32967      */
32968     selectPrevious : function(){
32969         var s = this.selNode || this.lastSelNode;
32970         if(!s){
32971             return null;
32972         }
32973         var ps = s.previousSibling;
32974         if(ps){
32975             if(!ps.isExpanded() || ps.childNodes.length < 1){
32976                 return this.select(ps);
32977             } else{
32978                 var lc = ps.lastChild;
32979                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32980                     lc = lc.lastChild;
32981                 }
32982                 return this.select(lc);
32983             }
32984         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32985             return this.select(s.parentNode);
32986         }
32987         return null;
32988     },
32989
32990     /**
32991      * Selects the node above the selected node in the tree, intelligently walking the nodes
32992      * @return TreeNode The new selection
32993      */
32994     selectNext : function(){
32995         var s = this.selNode || this.lastSelNode;
32996         if(!s){
32997             return null;
32998         }
32999         if(s.firstChild && s.isExpanded()){
33000              return this.select(s.firstChild);
33001          }else if(s.nextSibling){
33002              return this.select(s.nextSibling);
33003          }else if(s.parentNode){
33004             var newS = null;
33005             s.parentNode.bubble(function(){
33006                 if(this.nextSibling){
33007                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33008                     return false;
33009                 }
33010             });
33011             return newS;
33012          }
33013         return null;
33014     },
33015
33016     onKeyDown : function(e){
33017         var s = this.selNode || this.lastSelNode;
33018         // undesirable, but required
33019         var sm = this;
33020         if(!s){
33021             return;
33022         }
33023         var k = e.getKey();
33024         switch(k){
33025              case e.DOWN:
33026                  e.stopEvent();
33027                  this.selectNext();
33028              break;
33029              case e.UP:
33030                  e.stopEvent();
33031                  this.selectPrevious();
33032              break;
33033              case e.RIGHT:
33034                  e.preventDefault();
33035                  if(s.hasChildNodes()){
33036                      if(!s.isExpanded()){
33037                          s.expand();
33038                      }else if(s.firstChild){
33039                          this.select(s.firstChild, e);
33040                      }
33041                  }
33042              break;
33043              case e.LEFT:
33044                  e.preventDefault();
33045                  if(s.hasChildNodes() && s.isExpanded()){
33046                      s.collapse();
33047                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33048                      this.select(s.parentNode, e);
33049                  }
33050              break;
33051         };
33052     }
33053 });
33054
33055 /**
33056  * @class Roo.tree.MultiSelectionModel
33057  * @extends Roo.util.Observable
33058  * Multi selection for a TreePanel.
33059  * @param {Object} cfg Configuration
33060  */
33061 Roo.tree.MultiSelectionModel = function(){
33062    this.selNodes = [];
33063    this.selMap = {};
33064    this.addEvents({
33065        /**
33066         * @event selectionchange
33067         * Fires when the selected nodes change
33068         * @param {MultiSelectionModel} this
33069         * @param {Array} nodes Array of the selected nodes
33070         */
33071        "selectionchange" : true
33072    });
33073    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33074    
33075 };
33076
33077 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33078     init : function(tree){
33079         this.tree = tree;
33080         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33081         tree.on("click", this.onNodeClick, this);
33082     },
33083     
33084     onNodeClick : function(node, e){
33085         this.select(node, e, e.ctrlKey);
33086     },
33087     
33088     /**
33089      * Select a node.
33090      * @param {TreeNode} node The node to select
33091      * @param {EventObject} e (optional) An event associated with the selection
33092      * @param {Boolean} keepExisting True to retain existing selections
33093      * @return {TreeNode} The selected node
33094      */
33095     select : function(node, e, keepExisting){
33096         if(keepExisting !== true){
33097             this.clearSelections(true);
33098         }
33099         if(this.isSelected(node)){
33100             this.lastSelNode = node;
33101             return node;
33102         }
33103         this.selNodes.push(node);
33104         this.selMap[node.id] = node;
33105         this.lastSelNode = node;
33106         node.ui.onSelectedChange(true);
33107         this.fireEvent("selectionchange", this, this.selNodes);
33108         return node;
33109     },
33110     
33111     /**
33112      * Deselect a node.
33113      * @param {TreeNode} node The node to unselect
33114      */
33115     unselect : function(node){
33116         if(this.selMap[node.id]){
33117             node.ui.onSelectedChange(false);
33118             var sn = this.selNodes;
33119             var index = -1;
33120             if(sn.indexOf){
33121                 index = sn.indexOf(node);
33122             }else{
33123                 for(var i = 0, len = sn.length; i < len; i++){
33124                     if(sn[i] == node){
33125                         index = i;
33126                         break;
33127                     }
33128                 }
33129             }
33130             if(index != -1){
33131                 this.selNodes.splice(index, 1);
33132             }
33133             delete this.selMap[node.id];
33134             this.fireEvent("selectionchange", this, this.selNodes);
33135         }
33136     },
33137     
33138     /**
33139      * Clear all selections
33140      */
33141     clearSelections : function(suppressEvent){
33142         var sn = this.selNodes;
33143         if(sn.length > 0){
33144             for(var i = 0, len = sn.length; i < len; i++){
33145                 sn[i].ui.onSelectedChange(false);
33146             }
33147             this.selNodes = [];
33148             this.selMap = {};
33149             if(suppressEvent !== true){
33150                 this.fireEvent("selectionchange", this, this.selNodes);
33151             }
33152         }
33153     },
33154     
33155     /**
33156      * Returns true if the node is selected
33157      * @param {TreeNode} node The node to check
33158      * @return {Boolean}
33159      */
33160     isSelected : function(node){
33161         return this.selMap[node.id] ? true : false;  
33162     },
33163     
33164     /**
33165      * Returns an array of the selected nodes
33166      * @return {Array}
33167      */
33168     getSelectedNodes : function(){
33169         return this.selNodes;    
33170     },
33171
33172     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33173
33174     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33175
33176     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33177 });/*
33178  * Based on:
33179  * Ext JS Library 1.1.1
33180  * Copyright(c) 2006-2007, Ext JS, LLC.
33181  *
33182  * Originally Released Under LGPL - original licence link has changed is not relivant.
33183  *
33184  * Fork - LGPL
33185  * <script type="text/javascript">
33186  */
33187  
33188 /**
33189  * @class Roo.tree.TreeNode
33190  * @extends Roo.data.Node
33191  * @cfg {String} text The text for this node
33192  * @cfg {Boolean} expanded true to start the node expanded
33193  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33194  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33195  * @cfg {Boolean} disabled true to start the node disabled
33196  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33197  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33198  * @cfg {String} cls A css class to be added to the node
33199  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33200  * @cfg {String} href URL of the link used for the node (defaults to #)
33201  * @cfg {String} hrefTarget target frame for the link
33202  * @cfg {String} qtip An Ext QuickTip for the node
33203  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33204  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33205  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33206  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33207  * (defaults to undefined with no checkbox rendered)
33208  * @constructor
33209  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33210  */
33211 Roo.tree.TreeNode = function(attributes){
33212     attributes = attributes || {};
33213     if(typeof attributes == "string"){
33214         attributes = {text: attributes};
33215     }
33216     this.childrenRendered = false;
33217     this.rendered = false;
33218     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33219     this.expanded = attributes.expanded === true;
33220     this.isTarget = attributes.isTarget !== false;
33221     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33222     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33223
33224     /**
33225      * Read-only. The text for this node. To change it use setText().
33226      * @type String
33227      */
33228     this.text = attributes.text;
33229     /**
33230      * True if this node is disabled.
33231      * @type Boolean
33232      */
33233     this.disabled = attributes.disabled === true;
33234
33235     this.addEvents({
33236         /**
33237         * @event textchange
33238         * Fires when the text for this node is changed
33239         * @param {Node} this This node
33240         * @param {String} text The new text
33241         * @param {String} oldText The old text
33242         */
33243         "textchange" : true,
33244         /**
33245         * @event beforeexpand
33246         * Fires before this node is expanded, return false to cancel.
33247         * @param {Node} this This node
33248         * @param {Boolean} deep
33249         * @param {Boolean} anim
33250         */
33251         "beforeexpand" : true,
33252         /**
33253         * @event beforecollapse
33254         * Fires before this node is collapsed, return false to cancel.
33255         * @param {Node} this This node
33256         * @param {Boolean} deep
33257         * @param {Boolean} anim
33258         */
33259         "beforecollapse" : true,
33260         /**
33261         * @event expand
33262         * Fires when this node is expanded
33263         * @param {Node} this This node
33264         */
33265         "expand" : true,
33266         /**
33267         * @event disabledchange
33268         * Fires when the disabled status of this node changes
33269         * @param {Node} this This node
33270         * @param {Boolean} disabled
33271         */
33272         "disabledchange" : true,
33273         /**
33274         * @event collapse
33275         * Fires when this node is collapsed
33276         * @param {Node} this This node
33277         */
33278         "collapse" : true,
33279         /**
33280         * @event beforeclick
33281         * Fires before click processing. Return false to cancel the default action.
33282         * @param {Node} this This node
33283         * @param {Roo.EventObject} e The event object
33284         */
33285         "beforeclick":true,
33286         /**
33287         * @event checkchange
33288         * Fires when a node with a checkbox's checked property changes
33289         * @param {Node} this This node
33290         * @param {Boolean} checked
33291         */
33292         "checkchange":true,
33293         /**
33294         * @event click
33295         * Fires when this node is clicked
33296         * @param {Node} this This node
33297         * @param {Roo.EventObject} e The event object
33298         */
33299         "click":true,
33300         /**
33301         * @event dblclick
33302         * Fires when this node is double clicked
33303         * @param {Node} this This node
33304         * @param {Roo.EventObject} e The event object
33305         */
33306         "dblclick":true,
33307         /**
33308         * @event contextmenu
33309         * Fires when this node is right clicked
33310         * @param {Node} this This node
33311         * @param {Roo.EventObject} e The event object
33312         */
33313         "contextmenu":true,
33314         /**
33315         * @event beforechildrenrendered
33316         * Fires right before the child nodes for this node are rendered
33317         * @param {Node} this This node
33318         */
33319         "beforechildrenrendered":true
33320     });
33321
33322     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33323
33324     /**
33325      * Read-only. The UI for this node
33326      * @type TreeNodeUI
33327      */
33328     this.ui = new uiClass(this);
33329     
33330     // finally support items[]
33331     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33332         return;
33333     }
33334     
33335     
33336     Roo.each(this.attributes.items, function(c) {
33337         this.appendChild(Roo.factory(c,Roo.Tree));
33338     }, this);
33339     delete this.attributes.items;
33340     
33341     
33342     
33343 };
33344 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33345     preventHScroll: true,
33346     /**
33347      * Returns true if this node is expanded
33348      * @return {Boolean}
33349      */
33350     isExpanded : function(){
33351         return this.expanded;
33352     },
33353
33354     /**
33355      * Returns the UI object for this node
33356      * @return {TreeNodeUI}
33357      */
33358     getUI : function(){
33359         return this.ui;
33360     },
33361
33362     // private override
33363     setFirstChild : function(node){
33364         var of = this.firstChild;
33365         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33366         if(this.childrenRendered && of && node != of){
33367             of.renderIndent(true, true);
33368         }
33369         if(this.rendered){
33370             this.renderIndent(true, true);
33371         }
33372     },
33373
33374     // private override
33375     setLastChild : function(node){
33376         var ol = this.lastChild;
33377         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33378         if(this.childrenRendered && ol && node != ol){
33379             ol.renderIndent(true, true);
33380         }
33381         if(this.rendered){
33382             this.renderIndent(true, true);
33383         }
33384     },
33385
33386     // these methods are overridden to provide lazy rendering support
33387     // private override
33388     appendChild : function()
33389     {
33390         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33391         if(node && this.childrenRendered){
33392             node.render();
33393         }
33394         this.ui.updateExpandIcon();
33395         return node;
33396     },
33397
33398     // private override
33399     removeChild : function(node){
33400         this.ownerTree.getSelectionModel().unselect(node);
33401         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33402         // if it's been rendered remove dom node
33403         if(this.childrenRendered){
33404             node.ui.remove();
33405         }
33406         if(this.childNodes.length < 1){
33407             this.collapse(false, false);
33408         }else{
33409             this.ui.updateExpandIcon();
33410         }
33411         if(!this.firstChild) {
33412             this.childrenRendered = false;
33413         }
33414         return node;
33415     },
33416
33417     // private override
33418     insertBefore : function(node, refNode){
33419         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33420         if(newNode && refNode && this.childrenRendered){
33421             node.render();
33422         }
33423         this.ui.updateExpandIcon();
33424         return newNode;
33425     },
33426
33427     /**
33428      * Sets the text for this node
33429      * @param {String} text
33430      */
33431     setText : function(text){
33432         var oldText = this.text;
33433         this.text = text;
33434         this.attributes.text = text;
33435         if(this.rendered){ // event without subscribing
33436             this.ui.onTextChange(this, text, oldText);
33437         }
33438         this.fireEvent("textchange", this, text, oldText);
33439     },
33440
33441     /**
33442      * Triggers selection of this node
33443      */
33444     select : function(){
33445         this.getOwnerTree().getSelectionModel().select(this);
33446     },
33447
33448     /**
33449      * Triggers deselection of this node
33450      */
33451     unselect : function(){
33452         this.getOwnerTree().getSelectionModel().unselect(this);
33453     },
33454
33455     /**
33456      * Returns true if this node is selected
33457      * @return {Boolean}
33458      */
33459     isSelected : function(){
33460         return this.getOwnerTree().getSelectionModel().isSelected(this);
33461     },
33462
33463     /**
33464      * Expand this node.
33465      * @param {Boolean} deep (optional) True to expand all children as well
33466      * @param {Boolean} anim (optional) false to cancel the default animation
33467      * @param {Function} callback (optional) A callback to be called when
33468      * expanding this node completes (does not wait for deep expand to complete).
33469      * Called with 1 parameter, this node.
33470      */
33471     expand : function(deep, anim, callback){
33472         if(!this.expanded){
33473             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33474                 return;
33475             }
33476             if(!this.childrenRendered){
33477                 this.renderChildren();
33478             }
33479             this.expanded = true;
33480             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33481                 this.ui.animExpand(function(){
33482                     this.fireEvent("expand", this);
33483                     if(typeof callback == "function"){
33484                         callback(this);
33485                     }
33486                     if(deep === true){
33487                         this.expandChildNodes(true);
33488                     }
33489                 }.createDelegate(this));
33490                 return;
33491             }else{
33492                 this.ui.expand();
33493                 this.fireEvent("expand", this);
33494                 if(typeof callback == "function"){
33495                     callback(this);
33496                 }
33497             }
33498         }else{
33499            if(typeof callback == "function"){
33500                callback(this);
33501            }
33502         }
33503         if(deep === true){
33504             this.expandChildNodes(true);
33505         }
33506     },
33507
33508     isHiddenRoot : function(){
33509         return this.isRoot && !this.getOwnerTree().rootVisible;
33510     },
33511
33512     /**
33513      * Collapse this node.
33514      * @param {Boolean} deep (optional) True to collapse all children as well
33515      * @param {Boolean} anim (optional) false to cancel the default animation
33516      */
33517     collapse : function(deep, anim){
33518         if(this.expanded && !this.isHiddenRoot()){
33519             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33520                 return;
33521             }
33522             this.expanded = false;
33523             if((this.getOwnerTree().animate && anim !== false) || anim){
33524                 this.ui.animCollapse(function(){
33525                     this.fireEvent("collapse", this);
33526                     if(deep === true){
33527                         this.collapseChildNodes(true);
33528                     }
33529                 }.createDelegate(this));
33530                 return;
33531             }else{
33532                 this.ui.collapse();
33533                 this.fireEvent("collapse", this);
33534             }
33535         }
33536         if(deep === true){
33537             var cs = this.childNodes;
33538             for(var i = 0, len = cs.length; i < len; i++) {
33539                 cs[i].collapse(true, false);
33540             }
33541         }
33542     },
33543
33544     // private
33545     delayedExpand : function(delay){
33546         if(!this.expandProcId){
33547             this.expandProcId = this.expand.defer(delay, this);
33548         }
33549     },
33550
33551     // private
33552     cancelExpand : function(){
33553         if(this.expandProcId){
33554             clearTimeout(this.expandProcId);
33555         }
33556         this.expandProcId = false;
33557     },
33558
33559     /**
33560      * Toggles expanded/collapsed state of the node
33561      */
33562     toggle : function(){
33563         if(this.expanded){
33564             this.collapse();
33565         }else{
33566             this.expand();
33567         }
33568     },
33569
33570     /**
33571      * Ensures all parent nodes are expanded
33572      */
33573     ensureVisible : function(callback){
33574         var tree = this.getOwnerTree();
33575         tree.expandPath(this.parentNode.getPath(), false, function(){
33576             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33577             Roo.callback(callback);
33578         }.createDelegate(this));
33579     },
33580
33581     /**
33582      * Expand all child nodes
33583      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33584      */
33585     expandChildNodes : function(deep){
33586         var cs = this.childNodes;
33587         for(var i = 0, len = cs.length; i < len; i++) {
33588                 cs[i].expand(deep);
33589         }
33590     },
33591
33592     /**
33593      * Collapse all child nodes
33594      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33595      */
33596     collapseChildNodes : function(deep){
33597         var cs = this.childNodes;
33598         for(var i = 0, len = cs.length; i < len; i++) {
33599                 cs[i].collapse(deep);
33600         }
33601     },
33602
33603     /**
33604      * Disables this node
33605      */
33606     disable : function(){
33607         this.disabled = true;
33608         this.unselect();
33609         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33610             this.ui.onDisableChange(this, true);
33611         }
33612         this.fireEvent("disabledchange", this, true);
33613     },
33614
33615     /**
33616      * Enables this node
33617      */
33618     enable : function(){
33619         this.disabled = false;
33620         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33621             this.ui.onDisableChange(this, false);
33622         }
33623         this.fireEvent("disabledchange", this, false);
33624     },
33625
33626     // private
33627     renderChildren : function(suppressEvent){
33628         if(suppressEvent !== false){
33629             this.fireEvent("beforechildrenrendered", this);
33630         }
33631         var cs = this.childNodes;
33632         for(var i = 0, len = cs.length; i < len; i++){
33633             cs[i].render(true);
33634         }
33635         this.childrenRendered = true;
33636     },
33637
33638     // private
33639     sort : function(fn, scope){
33640         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33641         if(this.childrenRendered){
33642             var cs = this.childNodes;
33643             for(var i = 0, len = cs.length; i < len; i++){
33644                 cs[i].render(true);
33645             }
33646         }
33647     },
33648
33649     // private
33650     render : function(bulkRender){
33651         this.ui.render(bulkRender);
33652         if(!this.rendered){
33653             this.rendered = true;
33654             if(this.expanded){
33655                 this.expanded = false;
33656                 this.expand(false, false);
33657             }
33658         }
33659     },
33660
33661     // private
33662     renderIndent : function(deep, refresh){
33663         if(refresh){
33664             this.ui.childIndent = null;
33665         }
33666         this.ui.renderIndent();
33667         if(deep === true && this.childrenRendered){
33668             var cs = this.childNodes;
33669             for(var i = 0, len = cs.length; i < len; i++){
33670                 cs[i].renderIndent(true, refresh);
33671             }
33672         }
33673     }
33674 });/*
33675  * Based on:
33676  * Ext JS Library 1.1.1
33677  * Copyright(c) 2006-2007, Ext JS, LLC.
33678  *
33679  * Originally Released Under LGPL - original licence link has changed is not relivant.
33680  *
33681  * Fork - LGPL
33682  * <script type="text/javascript">
33683  */
33684  
33685 /**
33686  * @class Roo.tree.AsyncTreeNode
33687  * @extends Roo.tree.TreeNode
33688  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33689  * @constructor
33690  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33691  */
33692  Roo.tree.AsyncTreeNode = function(config){
33693     this.loaded = false;
33694     this.loading = false;
33695     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33696     /**
33697     * @event beforeload
33698     * Fires before this node is loaded, return false to cancel
33699     * @param {Node} this This node
33700     */
33701     this.addEvents({'beforeload':true, 'load': true});
33702     /**
33703     * @event load
33704     * Fires when this node is loaded
33705     * @param {Node} this This node
33706     */
33707     /**
33708      * The loader used by this node (defaults to using the tree's defined loader)
33709      * @type TreeLoader
33710      * @property loader
33711      */
33712 };
33713 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33714     expand : function(deep, anim, callback){
33715         if(this.loading){ // if an async load is already running, waiting til it's done
33716             var timer;
33717             var f = function(){
33718                 if(!this.loading){ // done loading
33719                     clearInterval(timer);
33720                     this.expand(deep, anim, callback);
33721                 }
33722             }.createDelegate(this);
33723             timer = setInterval(f, 200);
33724             return;
33725         }
33726         if(!this.loaded){
33727             if(this.fireEvent("beforeload", this) === false){
33728                 return;
33729             }
33730             this.loading = true;
33731             this.ui.beforeLoad(this);
33732             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33733             if(loader){
33734                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33735                 return;
33736             }
33737         }
33738         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33739     },
33740     
33741     /**
33742      * Returns true if this node is currently loading
33743      * @return {Boolean}
33744      */
33745     isLoading : function(){
33746         return this.loading;  
33747     },
33748     
33749     loadComplete : function(deep, anim, callback){
33750         this.loading = false;
33751         this.loaded = true;
33752         this.ui.afterLoad(this);
33753         this.fireEvent("load", this);
33754         this.expand(deep, anim, callback);
33755     },
33756     
33757     /**
33758      * Returns true if this node has been loaded
33759      * @return {Boolean}
33760      */
33761     isLoaded : function(){
33762         return this.loaded;
33763     },
33764     
33765     hasChildNodes : function(){
33766         if(!this.isLeaf() && !this.loaded){
33767             return true;
33768         }else{
33769             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33770         }
33771     },
33772
33773     /**
33774      * Trigger a reload for this node
33775      * @param {Function} callback
33776      */
33777     reload : function(callback){
33778         this.collapse(false, false);
33779         while(this.firstChild){
33780             this.removeChild(this.firstChild);
33781         }
33782         this.childrenRendered = false;
33783         this.loaded = false;
33784         if(this.isHiddenRoot()){
33785             this.expanded = false;
33786         }
33787         this.expand(false, false, callback);
33788     }
33789 });/*
33790  * Based on:
33791  * Ext JS Library 1.1.1
33792  * Copyright(c) 2006-2007, Ext JS, LLC.
33793  *
33794  * Originally Released Under LGPL - original licence link has changed is not relivant.
33795  *
33796  * Fork - LGPL
33797  * <script type="text/javascript">
33798  */
33799  
33800 /**
33801  * @class Roo.tree.TreeNodeUI
33802  * @constructor
33803  * @param {Object} node The node to render
33804  * The TreeNode UI implementation is separate from the
33805  * tree implementation. Unless you are customizing the tree UI,
33806  * you should never have to use this directly.
33807  */
33808 Roo.tree.TreeNodeUI = function(node){
33809     this.node = node;
33810     this.rendered = false;
33811     this.animating = false;
33812     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33813 };
33814
33815 Roo.tree.TreeNodeUI.prototype = {
33816     removeChild : function(node){
33817         if(this.rendered){
33818             this.ctNode.removeChild(node.ui.getEl());
33819         }
33820     },
33821
33822     beforeLoad : function(){
33823          this.addClass("x-tree-node-loading");
33824     },
33825
33826     afterLoad : function(){
33827          this.removeClass("x-tree-node-loading");
33828     },
33829
33830     onTextChange : function(node, text, oldText){
33831         if(this.rendered){
33832             this.textNode.innerHTML = text;
33833         }
33834     },
33835
33836     onDisableChange : function(node, state){
33837         this.disabled = state;
33838         if(state){
33839             this.addClass("x-tree-node-disabled");
33840         }else{
33841             this.removeClass("x-tree-node-disabled");
33842         }
33843     },
33844
33845     onSelectedChange : function(state){
33846         if(state){
33847             this.focus();
33848             this.addClass("x-tree-selected");
33849         }else{
33850             //this.blur();
33851             this.removeClass("x-tree-selected");
33852         }
33853     },
33854
33855     onMove : function(tree, node, oldParent, newParent, index, refNode){
33856         this.childIndent = null;
33857         if(this.rendered){
33858             var targetNode = newParent.ui.getContainer();
33859             if(!targetNode){//target not rendered
33860                 this.holder = document.createElement("div");
33861                 this.holder.appendChild(this.wrap);
33862                 return;
33863             }
33864             var insertBefore = refNode ? refNode.ui.getEl() : null;
33865             if(insertBefore){
33866                 targetNode.insertBefore(this.wrap, insertBefore);
33867             }else{
33868                 targetNode.appendChild(this.wrap);
33869             }
33870             this.node.renderIndent(true);
33871         }
33872     },
33873
33874     addClass : function(cls){
33875         if(this.elNode){
33876             Roo.fly(this.elNode).addClass(cls);
33877         }
33878     },
33879
33880     removeClass : function(cls){
33881         if(this.elNode){
33882             Roo.fly(this.elNode).removeClass(cls);
33883         }
33884     },
33885
33886     remove : function(){
33887         if(this.rendered){
33888             this.holder = document.createElement("div");
33889             this.holder.appendChild(this.wrap);
33890         }
33891     },
33892
33893     fireEvent : function(){
33894         return this.node.fireEvent.apply(this.node, arguments);
33895     },
33896
33897     initEvents : function(){
33898         this.node.on("move", this.onMove, this);
33899         var E = Roo.EventManager;
33900         var a = this.anchor;
33901
33902         var el = Roo.fly(a, '_treeui');
33903
33904         if(Roo.isOpera){ // opera render bug ignores the CSS
33905             el.setStyle("text-decoration", "none");
33906         }
33907
33908         el.on("click", this.onClick, this);
33909         el.on("dblclick", this.onDblClick, this);
33910
33911         if(this.checkbox){
33912             Roo.EventManager.on(this.checkbox,
33913                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33914         }
33915
33916         el.on("contextmenu", this.onContextMenu, this);
33917
33918         var icon = Roo.fly(this.iconNode);
33919         icon.on("click", this.onClick, this);
33920         icon.on("dblclick", this.onDblClick, this);
33921         icon.on("contextmenu", this.onContextMenu, this);
33922         E.on(this.ecNode, "click", this.ecClick, this, true);
33923
33924         if(this.node.disabled){
33925             this.addClass("x-tree-node-disabled");
33926         }
33927         if(this.node.hidden){
33928             this.addClass("x-tree-node-disabled");
33929         }
33930         var ot = this.node.getOwnerTree();
33931         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33932         if(dd && (!this.node.isRoot || ot.rootVisible)){
33933             Roo.dd.Registry.register(this.elNode, {
33934                 node: this.node,
33935                 handles: this.getDDHandles(),
33936                 isHandle: false
33937             });
33938         }
33939     },
33940
33941     getDDHandles : function(){
33942         return [this.iconNode, this.textNode];
33943     },
33944
33945     hide : function(){
33946         if(this.rendered){
33947             this.wrap.style.display = "none";
33948         }
33949     },
33950
33951     show : function(){
33952         if(this.rendered){
33953             this.wrap.style.display = "";
33954         }
33955     },
33956
33957     onContextMenu : function(e){
33958         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33959             e.preventDefault();
33960             this.focus();
33961             this.fireEvent("contextmenu", this.node, e);
33962         }
33963     },
33964
33965     onClick : function(e){
33966         if(this.dropping){
33967             e.stopEvent();
33968             return;
33969         }
33970         if(this.fireEvent("beforeclick", this.node, e) !== false){
33971             if(!this.disabled && this.node.attributes.href){
33972                 this.fireEvent("click", this.node, e);
33973                 return;
33974             }
33975             e.preventDefault();
33976             if(this.disabled){
33977                 return;
33978             }
33979
33980             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33981                 this.node.toggle();
33982             }
33983
33984             this.fireEvent("click", this.node, e);
33985         }else{
33986             e.stopEvent();
33987         }
33988     },
33989
33990     onDblClick : function(e){
33991         e.preventDefault();
33992         if(this.disabled){
33993             return;
33994         }
33995         if(this.checkbox){
33996             this.toggleCheck();
33997         }
33998         if(!this.animating && this.node.hasChildNodes()){
33999             this.node.toggle();
34000         }
34001         this.fireEvent("dblclick", this.node, e);
34002     },
34003
34004     onCheckChange : function(){
34005         var checked = this.checkbox.checked;
34006         this.node.attributes.checked = checked;
34007         this.fireEvent('checkchange', this.node, checked);
34008     },
34009
34010     ecClick : function(e){
34011         if(!this.animating && this.node.hasChildNodes()){
34012             this.node.toggle();
34013         }
34014     },
34015
34016     startDrop : function(){
34017         this.dropping = true;
34018     },
34019
34020     // delayed drop so the click event doesn't get fired on a drop
34021     endDrop : function(){
34022        setTimeout(function(){
34023            this.dropping = false;
34024        }.createDelegate(this), 50);
34025     },
34026
34027     expand : function(){
34028         this.updateExpandIcon();
34029         this.ctNode.style.display = "";
34030     },
34031
34032     focus : function(){
34033         if(!this.node.preventHScroll){
34034             try{this.anchor.focus();
34035             }catch(e){}
34036         }else if(!Roo.isIE){
34037             try{
34038                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34039                 var l = noscroll.scrollLeft;
34040                 this.anchor.focus();
34041                 noscroll.scrollLeft = l;
34042             }catch(e){}
34043         }
34044     },
34045
34046     toggleCheck : function(value){
34047         var cb = this.checkbox;
34048         if(cb){
34049             cb.checked = (value === undefined ? !cb.checked : value);
34050         }
34051     },
34052
34053     blur : function(){
34054         try{
34055             this.anchor.blur();
34056         }catch(e){}
34057     },
34058
34059     animExpand : function(callback){
34060         var ct = Roo.get(this.ctNode);
34061         ct.stopFx();
34062         if(!this.node.hasChildNodes()){
34063             this.updateExpandIcon();
34064             this.ctNode.style.display = "";
34065             Roo.callback(callback);
34066             return;
34067         }
34068         this.animating = true;
34069         this.updateExpandIcon();
34070
34071         ct.slideIn('t', {
34072            callback : function(){
34073                this.animating = false;
34074                Roo.callback(callback);
34075             },
34076             scope: this,
34077             duration: this.node.ownerTree.duration || .25
34078         });
34079     },
34080
34081     highlight : function(){
34082         var tree = this.node.getOwnerTree();
34083         Roo.fly(this.wrap).highlight(
34084             tree.hlColor || "C3DAF9",
34085             {endColor: tree.hlBaseColor}
34086         );
34087     },
34088
34089     collapse : function(){
34090         this.updateExpandIcon();
34091         this.ctNode.style.display = "none";
34092     },
34093
34094     animCollapse : function(callback){
34095         var ct = Roo.get(this.ctNode);
34096         ct.enableDisplayMode('block');
34097         ct.stopFx();
34098
34099         this.animating = true;
34100         this.updateExpandIcon();
34101
34102         ct.slideOut('t', {
34103             callback : function(){
34104                this.animating = false;
34105                Roo.callback(callback);
34106             },
34107             scope: this,
34108             duration: this.node.ownerTree.duration || .25
34109         });
34110     },
34111
34112     getContainer : function(){
34113         return this.ctNode;
34114     },
34115
34116     getEl : function(){
34117         return this.wrap;
34118     },
34119
34120     appendDDGhost : function(ghostNode){
34121         ghostNode.appendChild(this.elNode.cloneNode(true));
34122     },
34123
34124     getDDRepairXY : function(){
34125         return Roo.lib.Dom.getXY(this.iconNode);
34126     },
34127
34128     onRender : function(){
34129         this.render();
34130     },
34131
34132     render : function(bulkRender){
34133         var n = this.node, a = n.attributes;
34134         var targetNode = n.parentNode ?
34135               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34136
34137         if(!this.rendered){
34138             this.rendered = true;
34139
34140             this.renderElements(n, a, targetNode, bulkRender);
34141
34142             if(a.qtip){
34143                if(this.textNode.setAttributeNS){
34144                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34145                    if(a.qtipTitle){
34146                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34147                    }
34148                }else{
34149                    this.textNode.setAttribute("ext:qtip", a.qtip);
34150                    if(a.qtipTitle){
34151                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34152                    }
34153                }
34154             }else if(a.qtipCfg){
34155                 a.qtipCfg.target = Roo.id(this.textNode);
34156                 Roo.QuickTips.register(a.qtipCfg);
34157             }
34158             this.initEvents();
34159             if(!this.node.expanded){
34160                 this.updateExpandIcon();
34161             }
34162         }else{
34163             if(bulkRender === true) {
34164                 targetNode.appendChild(this.wrap);
34165             }
34166         }
34167     },
34168
34169     renderElements : function(n, a, targetNode, bulkRender)
34170     {
34171         // add some indent caching, this helps performance when rendering a large tree
34172         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34173         var t = n.getOwnerTree();
34174         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34175         if (typeof(n.attributes.html) != 'undefined') {
34176             txt = n.attributes.html;
34177         }
34178         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34179         var cb = typeof a.checked == 'boolean';
34180         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34181         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34182             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34183             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34184             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34185             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34186             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34187              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34188                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34189             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34190             "</li>"];
34191
34192         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34193             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34194                                 n.nextSibling.ui.getEl(), buf.join(""));
34195         }else{
34196             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34197         }
34198
34199         this.elNode = this.wrap.childNodes[0];
34200         this.ctNode = this.wrap.childNodes[1];
34201         var cs = this.elNode.childNodes;
34202         this.indentNode = cs[0];
34203         this.ecNode = cs[1];
34204         this.iconNode = cs[2];
34205         var index = 3;
34206         if(cb){
34207             this.checkbox = cs[3];
34208             index++;
34209         }
34210         this.anchor = cs[index];
34211         this.textNode = cs[index].firstChild;
34212     },
34213
34214     getAnchor : function(){
34215         return this.anchor;
34216     },
34217
34218     getTextEl : function(){
34219         return this.textNode;
34220     },
34221
34222     getIconEl : function(){
34223         return this.iconNode;
34224     },
34225
34226     isChecked : function(){
34227         return this.checkbox ? this.checkbox.checked : false;
34228     },
34229
34230     updateExpandIcon : function(){
34231         if(this.rendered){
34232             var n = this.node, c1, c2;
34233             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34234             var hasChild = n.hasChildNodes();
34235             if(hasChild){
34236                 if(n.expanded){
34237                     cls += "-minus";
34238                     c1 = "x-tree-node-collapsed";
34239                     c2 = "x-tree-node-expanded";
34240                 }else{
34241                     cls += "-plus";
34242                     c1 = "x-tree-node-expanded";
34243                     c2 = "x-tree-node-collapsed";
34244                 }
34245                 if(this.wasLeaf){
34246                     this.removeClass("x-tree-node-leaf");
34247                     this.wasLeaf = false;
34248                 }
34249                 if(this.c1 != c1 || this.c2 != c2){
34250                     Roo.fly(this.elNode).replaceClass(c1, c2);
34251                     this.c1 = c1; this.c2 = c2;
34252                 }
34253             }else{
34254                 // this changes non-leafs into leafs if they have no children.
34255                 // it's not very rational behaviour..
34256                 
34257                 if(!this.wasLeaf && this.node.leaf){
34258                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34259                     delete this.c1;
34260                     delete this.c2;
34261                     this.wasLeaf = true;
34262                 }
34263             }
34264             var ecc = "x-tree-ec-icon "+cls;
34265             if(this.ecc != ecc){
34266                 this.ecNode.className = ecc;
34267                 this.ecc = ecc;
34268             }
34269         }
34270     },
34271
34272     getChildIndent : function(){
34273         if(!this.childIndent){
34274             var buf = [];
34275             var p = this.node;
34276             while(p){
34277                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34278                     if(!p.isLast()) {
34279                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34280                     } else {
34281                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34282                     }
34283                 }
34284                 p = p.parentNode;
34285             }
34286             this.childIndent = buf.join("");
34287         }
34288         return this.childIndent;
34289     },
34290
34291     renderIndent : function(){
34292         if(this.rendered){
34293             var indent = "";
34294             var p = this.node.parentNode;
34295             if(p){
34296                 indent = p.ui.getChildIndent();
34297             }
34298             if(this.indentMarkup != indent){ // don't rerender if not required
34299                 this.indentNode.innerHTML = indent;
34300                 this.indentMarkup = indent;
34301             }
34302             this.updateExpandIcon();
34303         }
34304     }
34305 };
34306
34307 Roo.tree.RootTreeNodeUI = function(){
34308     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34309 };
34310 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34311     render : function(){
34312         if(!this.rendered){
34313             var targetNode = this.node.ownerTree.innerCt.dom;
34314             this.node.expanded = true;
34315             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34316             this.wrap = this.ctNode = targetNode.firstChild;
34317         }
34318     },
34319     collapse : function(){
34320     },
34321     expand : function(){
34322     }
34323 });/*
34324  * Based on:
34325  * Ext JS Library 1.1.1
34326  * Copyright(c) 2006-2007, Ext JS, LLC.
34327  *
34328  * Originally Released Under LGPL - original licence link has changed is not relivant.
34329  *
34330  * Fork - LGPL
34331  * <script type="text/javascript">
34332  */
34333 /**
34334  * @class Roo.tree.TreeLoader
34335  * @extends Roo.util.Observable
34336  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34337  * nodes from a specified URL. The response must be a javascript Array definition
34338  * who's elements are node definition objects. eg:
34339  * <pre><code>
34340 {  success : true,
34341    data :      [
34342    
34343     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34344     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34345     ]
34346 }
34347
34348
34349 </code></pre>
34350  * <br><br>
34351  * The old style respose with just an array is still supported, but not recommended.
34352  * <br><br>
34353  *
34354  * A server request is sent, and child nodes are loaded only when a node is expanded.
34355  * The loading node's id is passed to the server under the parameter name "node" to
34356  * enable the server to produce the correct child nodes.
34357  * <br><br>
34358  * To pass extra parameters, an event handler may be attached to the "beforeload"
34359  * event, and the parameters specified in the TreeLoader's baseParams property:
34360  * <pre><code>
34361     myTreeLoader.on("beforeload", function(treeLoader, node) {
34362         this.baseParams.category = node.attributes.category;
34363     }, this);
34364 </code></pre><
34365  * This would pass an HTTP parameter called "category" to the server containing
34366  * the value of the Node's "category" attribute.
34367  * @constructor
34368  * Creates a new Treeloader.
34369  * @param {Object} config A config object containing config properties.
34370  */
34371 Roo.tree.TreeLoader = function(config){
34372     this.baseParams = {};
34373     this.requestMethod = "POST";
34374     Roo.apply(this, config);
34375
34376     this.addEvents({
34377     
34378         /**
34379          * @event beforeload
34380          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34381          * @param {Object} This TreeLoader object.
34382          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34383          * @param {Object} callback The callback function specified in the {@link #load} call.
34384          */
34385         beforeload : true,
34386         /**
34387          * @event load
34388          * Fires when the node has been successfuly loaded.
34389          * @param {Object} This TreeLoader object.
34390          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34391          * @param {Object} response The response object containing the data from the server.
34392          */
34393         load : true,
34394         /**
34395          * @event loadexception
34396          * Fires if the network request failed.
34397          * @param {Object} This TreeLoader object.
34398          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34399          * @param {Object} response The response object containing the data from the server.
34400          */
34401         loadexception : true,
34402         /**
34403          * @event create
34404          * Fires before a node is created, enabling you to return custom Node types 
34405          * @param {Object} This TreeLoader object.
34406          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34407          */
34408         create : true
34409     });
34410
34411     Roo.tree.TreeLoader.superclass.constructor.call(this);
34412 };
34413
34414 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34415     /**
34416     * @cfg {String} dataUrl The URL from which to request a Json string which
34417     * specifies an array of node definition object representing the child nodes
34418     * to be loaded.
34419     */
34420     /**
34421     * @cfg {String} requestMethod either GET or POST
34422     * defaults to POST (due to BC)
34423     * to be loaded.
34424     */
34425     /**
34426     * @cfg {Object} baseParams (optional) An object containing properties which
34427     * specify HTTP parameters to be passed to each request for child nodes.
34428     */
34429     /**
34430     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34431     * created by this loader. If the attributes sent by the server have an attribute in this object,
34432     * they take priority.
34433     */
34434     /**
34435     * @cfg {Object} uiProviders (optional) An object containing properties which
34436     * 
34437     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34438     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34439     * <i>uiProvider</i> attribute of a returned child node is a string rather
34440     * than a reference to a TreeNodeUI implementation, this that string value
34441     * is used as a property name in the uiProviders object. You can define the provider named
34442     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34443     */
34444     uiProviders : {},
34445
34446     /**
34447     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34448     * child nodes before loading.
34449     */
34450     clearOnLoad : true,
34451
34452     /**
34453     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34454     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34455     * Grid query { data : [ .....] }
34456     */
34457     
34458     root : false,
34459      /**
34460     * @cfg {String} queryParam (optional) 
34461     * Name of the query as it will be passed on the querystring (defaults to 'node')
34462     * eg. the request will be ?node=[id]
34463     */
34464     
34465     
34466     queryParam: false,
34467     
34468     /**
34469      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34470      * This is called automatically when a node is expanded, but may be used to reload
34471      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34472      * @param {Roo.tree.TreeNode} node
34473      * @param {Function} callback
34474      */
34475     load : function(node, callback){
34476         if(this.clearOnLoad){
34477             while(node.firstChild){
34478                 node.removeChild(node.firstChild);
34479             }
34480         }
34481         if(node.attributes.children){ // preloaded json children
34482             var cs = node.attributes.children;
34483             for(var i = 0, len = cs.length; i < len; i++){
34484                 node.appendChild(this.createNode(cs[i]));
34485             }
34486             if(typeof callback == "function"){
34487                 callback();
34488             }
34489         }else if(this.dataUrl){
34490             this.requestData(node, callback);
34491         }
34492     },
34493
34494     getParams: function(node){
34495         var buf = [], bp = this.baseParams;
34496         for(var key in bp){
34497             if(typeof bp[key] != "function"){
34498                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34499             }
34500         }
34501         var n = this.queryParam === false ? 'node' : this.queryParam;
34502         buf.push(n + "=", encodeURIComponent(node.id));
34503         return buf.join("");
34504     },
34505
34506     requestData : function(node, callback){
34507         if(this.fireEvent("beforeload", this, node, callback) !== false){
34508             this.transId = Roo.Ajax.request({
34509                 method:this.requestMethod,
34510                 url: this.dataUrl||this.url,
34511                 success: this.handleResponse,
34512                 failure: this.handleFailure,
34513                 scope: this,
34514                 argument: {callback: callback, node: node},
34515                 params: this.getParams(node)
34516             });
34517         }else{
34518             // if the load is cancelled, make sure we notify
34519             // the node that we are done
34520             if(typeof callback == "function"){
34521                 callback();
34522             }
34523         }
34524     },
34525
34526     isLoading : function(){
34527         return this.transId ? true : false;
34528     },
34529
34530     abort : function(){
34531         if(this.isLoading()){
34532             Roo.Ajax.abort(this.transId);
34533         }
34534     },
34535
34536     // private
34537     createNode : function(attr)
34538     {
34539         // apply baseAttrs, nice idea Corey!
34540         if(this.baseAttrs){
34541             Roo.applyIf(attr, this.baseAttrs);
34542         }
34543         if(this.applyLoader !== false){
34544             attr.loader = this;
34545         }
34546         // uiProvider = depreciated..
34547         
34548         if(typeof(attr.uiProvider) == 'string'){
34549            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34550                 /**  eval:var:attr */ eval(attr.uiProvider);
34551         }
34552         if(typeof(this.uiProviders['default']) != 'undefined') {
34553             attr.uiProvider = this.uiProviders['default'];
34554         }
34555         
34556         this.fireEvent('create', this, attr);
34557         
34558         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34559         return(attr.leaf ?
34560                         new Roo.tree.TreeNode(attr) :
34561                         new Roo.tree.AsyncTreeNode(attr));
34562     },
34563
34564     processResponse : function(response, node, callback)
34565     {
34566         var json = response.responseText;
34567         try {
34568             
34569             var o = Roo.decode(json);
34570             
34571             if (this.root === false && typeof(o.success) != undefined) {
34572                 this.root = 'data'; // the default behaviour for list like data..
34573                 }
34574                 
34575             if (this.root !== false &&  !o.success) {
34576                 // it's a failure condition.
34577                 var a = response.argument;
34578                 this.fireEvent("loadexception", this, a.node, response);
34579                 Roo.log("Load failed - should have a handler really");
34580                 return;
34581             }
34582             
34583             
34584             
34585             if (this.root !== false) {
34586                  o = o[this.root];
34587             }
34588             
34589             for(var i = 0, len = o.length; i < len; i++){
34590                 var n = this.createNode(o[i]);
34591                 if(n){
34592                     node.appendChild(n);
34593                 }
34594             }
34595             if(typeof callback == "function"){
34596                 callback(this, node);
34597             }
34598         }catch(e){
34599             this.handleFailure(response);
34600         }
34601     },
34602
34603     handleResponse : function(response){
34604         this.transId = false;
34605         var a = response.argument;
34606         this.processResponse(response, a.node, a.callback);
34607         this.fireEvent("load", this, a.node, response);
34608     },
34609
34610     handleFailure : function(response)
34611     {
34612         // should handle failure better..
34613         this.transId = false;
34614         var a = response.argument;
34615         this.fireEvent("loadexception", this, a.node, response);
34616         if(typeof a.callback == "function"){
34617             a.callback(this, a.node);
34618         }
34619     }
34620 });/*
34621  * Based on:
34622  * Ext JS Library 1.1.1
34623  * Copyright(c) 2006-2007, Ext JS, LLC.
34624  *
34625  * Originally Released Under LGPL - original licence link has changed is not relivant.
34626  *
34627  * Fork - LGPL
34628  * <script type="text/javascript">
34629  */
34630
34631 /**
34632 * @class Roo.tree.TreeFilter
34633 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34634 * @param {TreePanel} tree
34635 * @param {Object} config (optional)
34636  */
34637 Roo.tree.TreeFilter = function(tree, config){
34638     this.tree = tree;
34639     this.filtered = {};
34640     Roo.apply(this, config);
34641 };
34642
34643 Roo.tree.TreeFilter.prototype = {
34644     clearBlank:false,
34645     reverse:false,
34646     autoClear:false,
34647     remove:false,
34648
34649      /**
34650      * Filter the data by a specific attribute.
34651      * @param {String/RegExp} value Either string that the attribute value
34652      * should start with or a RegExp to test against the attribute
34653      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34654      * @param {TreeNode} startNode (optional) The node to start the filter at.
34655      */
34656     filter : function(value, attr, startNode){
34657         attr = attr || "text";
34658         var f;
34659         if(typeof value == "string"){
34660             var vlen = value.length;
34661             // auto clear empty filter
34662             if(vlen == 0 && this.clearBlank){
34663                 this.clear();
34664                 return;
34665             }
34666             value = value.toLowerCase();
34667             f = function(n){
34668                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34669             };
34670         }else if(value.exec){ // regex?
34671             f = function(n){
34672                 return value.test(n.attributes[attr]);
34673             };
34674         }else{
34675             throw 'Illegal filter type, must be string or regex';
34676         }
34677         this.filterBy(f, null, startNode);
34678         },
34679
34680     /**
34681      * Filter by a function. The passed function will be called with each
34682      * node in the tree (or from the startNode). If the function returns true, the node is kept
34683      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34684      * @param {Function} fn The filter function
34685      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34686      */
34687     filterBy : function(fn, scope, startNode){
34688         startNode = startNode || this.tree.root;
34689         if(this.autoClear){
34690             this.clear();
34691         }
34692         var af = this.filtered, rv = this.reverse;
34693         var f = function(n){
34694             if(n == startNode){
34695                 return true;
34696             }
34697             if(af[n.id]){
34698                 return false;
34699             }
34700             var m = fn.call(scope || n, n);
34701             if(!m || rv){
34702                 af[n.id] = n;
34703                 n.ui.hide();
34704                 return false;
34705             }
34706             return true;
34707         };
34708         startNode.cascade(f);
34709         if(this.remove){
34710            for(var id in af){
34711                if(typeof id != "function"){
34712                    var n = af[id];
34713                    if(n && n.parentNode){
34714                        n.parentNode.removeChild(n);
34715                    }
34716                }
34717            }
34718         }
34719     },
34720
34721     /**
34722      * Clears the current filter. Note: with the "remove" option
34723      * set a filter cannot be cleared.
34724      */
34725     clear : function(){
34726         var t = this.tree;
34727         var af = this.filtered;
34728         for(var id in af){
34729             if(typeof id != "function"){
34730                 var n = af[id];
34731                 if(n){
34732                     n.ui.show();
34733                 }
34734             }
34735         }
34736         this.filtered = {};
34737     }
34738 };
34739 /*
34740  * Based on:
34741  * Ext JS Library 1.1.1
34742  * Copyright(c) 2006-2007, Ext JS, LLC.
34743  *
34744  * Originally Released Under LGPL - original licence link has changed is not relivant.
34745  *
34746  * Fork - LGPL
34747  * <script type="text/javascript">
34748  */
34749  
34750
34751 /**
34752  * @class Roo.tree.TreeSorter
34753  * Provides sorting of nodes in a TreePanel
34754  * 
34755  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34756  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34757  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34758  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34759  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34760  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34761  * @constructor
34762  * @param {TreePanel} tree
34763  * @param {Object} config
34764  */
34765 Roo.tree.TreeSorter = function(tree, config){
34766     Roo.apply(this, config);
34767     tree.on("beforechildrenrendered", this.doSort, this);
34768     tree.on("append", this.updateSort, this);
34769     tree.on("insert", this.updateSort, this);
34770     
34771     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34772     var p = this.property || "text";
34773     var sortType = this.sortType;
34774     var fs = this.folderSort;
34775     var cs = this.caseSensitive === true;
34776     var leafAttr = this.leafAttr || 'leaf';
34777
34778     this.sortFn = function(n1, n2){
34779         if(fs){
34780             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34781                 return 1;
34782             }
34783             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34784                 return -1;
34785             }
34786         }
34787         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34788         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34789         if(v1 < v2){
34790                         return dsc ? +1 : -1;
34791                 }else if(v1 > v2){
34792                         return dsc ? -1 : +1;
34793         }else{
34794                 return 0;
34795         }
34796     };
34797 };
34798
34799 Roo.tree.TreeSorter.prototype = {
34800     doSort : function(node){
34801         node.sort(this.sortFn);
34802     },
34803     
34804     compareNodes : function(n1, n2){
34805         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34806     },
34807     
34808     updateSort : function(tree, node){
34809         if(node.childrenRendered){
34810             this.doSort.defer(1, this, [node]);
34811         }
34812     }
34813 };/*
34814  * Based on:
34815  * Ext JS Library 1.1.1
34816  * Copyright(c) 2006-2007, Ext JS, LLC.
34817  *
34818  * Originally Released Under LGPL - original licence link has changed is not relivant.
34819  *
34820  * Fork - LGPL
34821  * <script type="text/javascript">
34822  */
34823
34824 if(Roo.dd.DropZone){
34825     
34826 Roo.tree.TreeDropZone = function(tree, config){
34827     this.allowParentInsert = false;
34828     this.allowContainerDrop = false;
34829     this.appendOnly = false;
34830     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34831     this.tree = tree;
34832     this.lastInsertClass = "x-tree-no-status";
34833     this.dragOverData = {};
34834 };
34835
34836 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34837     ddGroup : "TreeDD",
34838     scroll:  true,
34839     
34840     expandDelay : 1000,
34841     
34842     expandNode : function(node){
34843         if(node.hasChildNodes() && !node.isExpanded()){
34844             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34845         }
34846     },
34847     
34848     queueExpand : function(node){
34849         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34850     },
34851     
34852     cancelExpand : function(){
34853         if(this.expandProcId){
34854             clearTimeout(this.expandProcId);
34855             this.expandProcId = false;
34856         }
34857     },
34858     
34859     isValidDropPoint : function(n, pt, dd, e, data){
34860         if(!n || !data){ return false; }
34861         var targetNode = n.node;
34862         var dropNode = data.node;
34863         // default drop rules
34864         if(!(targetNode && targetNode.isTarget && pt)){
34865             return false;
34866         }
34867         if(pt == "append" && targetNode.allowChildren === false){
34868             return false;
34869         }
34870         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34871             return false;
34872         }
34873         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34874             return false;
34875         }
34876         // reuse the object
34877         var overEvent = this.dragOverData;
34878         overEvent.tree = this.tree;
34879         overEvent.target = targetNode;
34880         overEvent.data = data;
34881         overEvent.point = pt;
34882         overEvent.source = dd;
34883         overEvent.rawEvent = e;
34884         overEvent.dropNode = dropNode;
34885         overEvent.cancel = false;  
34886         var result = this.tree.fireEvent("nodedragover", overEvent);
34887         return overEvent.cancel === false && result !== false;
34888     },
34889     
34890     getDropPoint : function(e, n, dd)
34891     {
34892         var tn = n.node;
34893         if(tn.isRoot){
34894             return tn.allowChildren !== false ? "append" : false; // always append for root
34895         }
34896         var dragEl = n.ddel;
34897         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34898         var y = Roo.lib.Event.getPageY(e);
34899         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34900         
34901         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34902         var noAppend = tn.allowChildren === false;
34903         if(this.appendOnly || tn.parentNode.allowChildren === false){
34904             return noAppend ? false : "append";
34905         }
34906         var noBelow = false;
34907         if(!this.allowParentInsert){
34908             noBelow = tn.hasChildNodes() && tn.isExpanded();
34909         }
34910         var q = (b - t) / (noAppend ? 2 : 3);
34911         if(y >= t && y < (t + q)){
34912             return "above";
34913         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34914             return "below";
34915         }else{
34916             return "append";
34917         }
34918     },
34919     
34920     onNodeEnter : function(n, dd, e, data)
34921     {
34922         this.cancelExpand();
34923     },
34924     
34925     onNodeOver : function(n, dd, e, data)
34926     {
34927        
34928         var pt = this.getDropPoint(e, n, dd);
34929         var node = n.node;
34930         
34931         // auto node expand check
34932         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34933             this.queueExpand(node);
34934         }else if(pt != "append"){
34935             this.cancelExpand();
34936         }
34937         
34938         // set the insert point style on the target node
34939         var returnCls = this.dropNotAllowed;
34940         if(this.isValidDropPoint(n, pt, dd, e, data)){
34941            if(pt){
34942                var el = n.ddel;
34943                var cls;
34944                if(pt == "above"){
34945                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34946                    cls = "x-tree-drag-insert-above";
34947                }else if(pt == "below"){
34948                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34949                    cls = "x-tree-drag-insert-below";
34950                }else{
34951                    returnCls = "x-tree-drop-ok-append";
34952                    cls = "x-tree-drag-append";
34953                }
34954                if(this.lastInsertClass != cls){
34955                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34956                    this.lastInsertClass = cls;
34957                }
34958            }
34959        }
34960        return returnCls;
34961     },
34962     
34963     onNodeOut : function(n, dd, e, data){
34964         
34965         this.cancelExpand();
34966         this.removeDropIndicators(n);
34967     },
34968     
34969     onNodeDrop : function(n, dd, e, data){
34970         var point = this.getDropPoint(e, n, dd);
34971         var targetNode = n.node;
34972         targetNode.ui.startDrop();
34973         if(!this.isValidDropPoint(n, point, dd, e, data)){
34974             targetNode.ui.endDrop();
34975             return false;
34976         }
34977         // first try to find the drop node
34978         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34979         var dropEvent = {
34980             tree : this.tree,
34981             target: targetNode,
34982             data: data,
34983             point: point,
34984             source: dd,
34985             rawEvent: e,
34986             dropNode: dropNode,
34987             cancel: !dropNode   
34988         };
34989         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34990         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34991             targetNode.ui.endDrop();
34992             return false;
34993         }
34994         // allow target changing
34995         targetNode = dropEvent.target;
34996         if(point == "append" && !targetNode.isExpanded()){
34997             targetNode.expand(false, null, function(){
34998                 this.completeDrop(dropEvent);
34999             }.createDelegate(this));
35000         }else{
35001             this.completeDrop(dropEvent);
35002         }
35003         return true;
35004     },
35005     
35006     completeDrop : function(de){
35007         var ns = de.dropNode, p = de.point, t = de.target;
35008         if(!(ns instanceof Array)){
35009             ns = [ns];
35010         }
35011         var n;
35012         for(var i = 0, len = ns.length; i < len; i++){
35013             n = ns[i];
35014             if(p == "above"){
35015                 t.parentNode.insertBefore(n, t);
35016             }else if(p == "below"){
35017                 t.parentNode.insertBefore(n, t.nextSibling);
35018             }else{
35019                 t.appendChild(n);
35020             }
35021         }
35022         n.ui.focus();
35023         if(this.tree.hlDrop){
35024             n.ui.highlight();
35025         }
35026         t.ui.endDrop();
35027         this.tree.fireEvent("nodedrop", de);
35028     },
35029     
35030     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35031         if(this.tree.hlDrop){
35032             dropNode.ui.focus();
35033             dropNode.ui.highlight();
35034         }
35035         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35036     },
35037     
35038     getTree : function(){
35039         return this.tree;
35040     },
35041     
35042     removeDropIndicators : function(n){
35043         if(n && n.ddel){
35044             var el = n.ddel;
35045             Roo.fly(el).removeClass([
35046                     "x-tree-drag-insert-above",
35047                     "x-tree-drag-insert-below",
35048                     "x-tree-drag-append"]);
35049             this.lastInsertClass = "_noclass";
35050         }
35051     },
35052     
35053     beforeDragDrop : function(target, e, id){
35054         this.cancelExpand();
35055         return true;
35056     },
35057     
35058     afterRepair : function(data){
35059         if(data && Roo.enableFx){
35060             data.node.ui.highlight();
35061         }
35062         this.hideProxy();
35063     } 
35064     
35065 });
35066
35067 }
35068 /*
35069  * Based on:
35070  * Ext JS Library 1.1.1
35071  * Copyright(c) 2006-2007, Ext JS, LLC.
35072  *
35073  * Originally Released Under LGPL - original licence link has changed is not relivant.
35074  *
35075  * Fork - LGPL
35076  * <script type="text/javascript">
35077  */
35078  
35079
35080 if(Roo.dd.DragZone){
35081 Roo.tree.TreeDragZone = function(tree, config){
35082     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35083     this.tree = tree;
35084 };
35085
35086 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35087     ddGroup : "TreeDD",
35088    
35089     onBeforeDrag : function(data, e){
35090         var n = data.node;
35091         return n && n.draggable && !n.disabled;
35092     },
35093      
35094     
35095     onInitDrag : function(e){
35096         var data = this.dragData;
35097         this.tree.getSelectionModel().select(data.node);
35098         this.proxy.update("");
35099         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35100         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35101     },
35102     
35103     getRepairXY : function(e, data){
35104         return data.node.ui.getDDRepairXY();
35105     },
35106     
35107     onEndDrag : function(data, e){
35108         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35109         
35110         
35111     },
35112     
35113     onValidDrop : function(dd, e, id){
35114         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35115         this.hideProxy();
35116     },
35117     
35118     beforeInvalidDrop : function(e, id){
35119         // this scrolls the original position back into view
35120         var sm = this.tree.getSelectionModel();
35121         sm.clearSelections();
35122         sm.select(this.dragData.node);
35123     }
35124 });
35125 }/*
35126  * Based on:
35127  * Ext JS Library 1.1.1
35128  * Copyright(c) 2006-2007, Ext JS, LLC.
35129  *
35130  * Originally Released Under LGPL - original licence link has changed is not relivant.
35131  *
35132  * Fork - LGPL
35133  * <script type="text/javascript">
35134  */
35135 /**
35136  * @class Roo.tree.TreeEditor
35137  * @extends Roo.Editor
35138  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35139  * as the editor field.
35140  * @constructor
35141  * @param {Object} config (used to be the tree panel.)
35142  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35143  * 
35144  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35145  * @cfg {Roo.form.TextField|Object} field The field configuration
35146  *
35147  * 
35148  */
35149 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35150     var tree = config;
35151     var field;
35152     if (oldconfig) { // old style..
35153         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35154     } else {
35155         // new style..
35156         tree = config.tree;
35157         config.field = config.field  || {};
35158         config.field.xtype = 'TextField';
35159         field = Roo.factory(config.field, Roo.form);
35160     }
35161     config = config || {};
35162     
35163     
35164     this.addEvents({
35165         /**
35166          * @event beforenodeedit
35167          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35168          * false from the handler of this event.
35169          * @param {Editor} this
35170          * @param {Roo.tree.Node} node 
35171          */
35172         "beforenodeedit" : true
35173     });
35174     
35175     //Roo.log(config);
35176     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35177
35178     this.tree = tree;
35179
35180     tree.on('beforeclick', this.beforeNodeClick, this);
35181     tree.getTreeEl().on('mousedown', this.hide, this);
35182     this.on('complete', this.updateNode, this);
35183     this.on('beforestartedit', this.fitToTree, this);
35184     this.on('startedit', this.bindScroll, this, {delay:10});
35185     this.on('specialkey', this.onSpecialKey, this);
35186 };
35187
35188 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35189     /**
35190      * @cfg {String} alignment
35191      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35192      */
35193     alignment: "l-l",
35194     // inherit
35195     autoSize: false,
35196     /**
35197      * @cfg {Boolean} hideEl
35198      * True to hide the bound element while the editor is displayed (defaults to false)
35199      */
35200     hideEl : false,
35201     /**
35202      * @cfg {String} cls
35203      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35204      */
35205     cls: "x-small-editor x-tree-editor",
35206     /**
35207      * @cfg {Boolean} shim
35208      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35209      */
35210     shim:false,
35211     // inherit
35212     shadow:"frame",
35213     /**
35214      * @cfg {Number} maxWidth
35215      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35216      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35217      * scroll and client offsets into account prior to each edit.
35218      */
35219     maxWidth: 250,
35220
35221     editDelay : 350,
35222
35223     // private
35224     fitToTree : function(ed, el){
35225         var td = this.tree.getTreeEl().dom, nd = el.dom;
35226         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35227             td.scrollLeft = nd.offsetLeft;
35228         }
35229         var w = Math.min(
35230                 this.maxWidth,
35231                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35232         this.setSize(w, '');
35233         
35234         return this.fireEvent('beforenodeedit', this, this.editNode);
35235         
35236     },
35237
35238     // private
35239     triggerEdit : function(node){
35240         this.completeEdit();
35241         this.editNode = node;
35242         this.startEdit(node.ui.textNode, node.text);
35243     },
35244
35245     // private
35246     bindScroll : function(){
35247         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35248     },
35249
35250     // private
35251     beforeNodeClick : function(node, e){
35252         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35253         this.lastClick = new Date();
35254         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35255             e.stopEvent();
35256             this.triggerEdit(node);
35257             return false;
35258         }
35259         return true;
35260     },
35261
35262     // private
35263     updateNode : function(ed, value){
35264         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35265         this.editNode.setText(value);
35266     },
35267
35268     // private
35269     onHide : function(){
35270         Roo.tree.TreeEditor.superclass.onHide.call(this);
35271         if(this.editNode){
35272             this.editNode.ui.focus();
35273         }
35274     },
35275
35276     // private
35277     onSpecialKey : function(field, e){
35278         var k = e.getKey();
35279         if(k == e.ESC){
35280             e.stopEvent();
35281             this.cancelEdit();
35282         }else if(k == e.ENTER && !e.hasModifier()){
35283             e.stopEvent();
35284             this.completeEdit();
35285         }
35286     }
35287 });//<Script type="text/javascript">
35288 /*
35289  * Based on:
35290  * Ext JS Library 1.1.1
35291  * Copyright(c) 2006-2007, Ext JS, LLC.
35292  *
35293  * Originally Released Under LGPL - original licence link has changed is not relivant.
35294  *
35295  * Fork - LGPL
35296  * <script type="text/javascript">
35297  */
35298  
35299 /**
35300  * Not documented??? - probably should be...
35301  */
35302
35303 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35304     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35305     
35306     renderElements : function(n, a, targetNode, bulkRender){
35307         //consel.log("renderElements?");
35308         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35309
35310         var t = n.getOwnerTree();
35311         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35312         
35313         var cols = t.columns;
35314         var bw = t.borderWidth;
35315         var c = cols[0];
35316         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35317          var cb = typeof a.checked == "boolean";
35318         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35319         var colcls = 'x-t-' + tid + '-c0';
35320         var buf = [
35321             '<li class="x-tree-node">',
35322             
35323                 
35324                 '<div class="x-tree-node-el ', a.cls,'">',
35325                     // extran...
35326                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35327                 
35328                 
35329                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35330                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35331                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35332                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35333                            (a.iconCls ? ' '+a.iconCls : ''),
35334                            '" unselectable="on" />',
35335                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35336                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35337                              
35338                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35339                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35340                             '<span unselectable="on" qtip="' + tx + '">',
35341                              tx,
35342                              '</span></a>' ,
35343                     '</div>',
35344                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35345                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35346                  ];
35347         for(var i = 1, len = cols.length; i < len; i++){
35348             c = cols[i];
35349             colcls = 'x-t-' + tid + '-c' +i;
35350             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35351             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35352                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35353                       "</div>");
35354          }
35355          
35356          buf.push(
35357             '</a>',
35358             '<div class="x-clear"></div></div>',
35359             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35360             "</li>");
35361         
35362         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35363             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35364                                 n.nextSibling.ui.getEl(), buf.join(""));
35365         }else{
35366             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35367         }
35368         var el = this.wrap.firstChild;
35369         this.elRow = el;
35370         this.elNode = el.firstChild;
35371         this.ranchor = el.childNodes[1];
35372         this.ctNode = this.wrap.childNodes[1];
35373         var cs = el.firstChild.childNodes;
35374         this.indentNode = cs[0];
35375         this.ecNode = cs[1];
35376         this.iconNode = cs[2];
35377         var index = 3;
35378         if(cb){
35379             this.checkbox = cs[3];
35380             index++;
35381         }
35382         this.anchor = cs[index];
35383         
35384         this.textNode = cs[index].firstChild;
35385         
35386         //el.on("click", this.onClick, this);
35387         //el.on("dblclick", this.onDblClick, this);
35388         
35389         
35390        // console.log(this);
35391     },
35392     initEvents : function(){
35393         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35394         
35395             
35396         var a = this.ranchor;
35397
35398         var el = Roo.get(a);
35399
35400         if(Roo.isOpera){ // opera render bug ignores the CSS
35401             el.setStyle("text-decoration", "none");
35402         }
35403
35404         el.on("click", this.onClick, this);
35405         el.on("dblclick", this.onDblClick, this);
35406         el.on("contextmenu", this.onContextMenu, this);
35407         
35408     },
35409     
35410     /*onSelectedChange : function(state){
35411         if(state){
35412             this.focus();
35413             this.addClass("x-tree-selected");
35414         }else{
35415             //this.blur();
35416             this.removeClass("x-tree-selected");
35417         }
35418     },*/
35419     addClass : function(cls){
35420         if(this.elRow){
35421             Roo.fly(this.elRow).addClass(cls);
35422         }
35423         
35424     },
35425     
35426     
35427     removeClass : function(cls){
35428         if(this.elRow){
35429             Roo.fly(this.elRow).removeClass(cls);
35430         }
35431     }
35432
35433     
35434     
35435 });//<Script type="text/javascript">
35436
35437 /*
35438  * Based on:
35439  * Ext JS Library 1.1.1
35440  * Copyright(c) 2006-2007, Ext JS, LLC.
35441  *
35442  * Originally Released Under LGPL - original licence link has changed is not relivant.
35443  *
35444  * Fork - LGPL
35445  * <script type="text/javascript">
35446  */
35447  
35448
35449 /**
35450  * @class Roo.tree.ColumnTree
35451  * @extends Roo.data.TreePanel
35452  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35453  * @cfg {int} borderWidth  compined right/left border allowance
35454  * @constructor
35455  * @param {String/HTMLElement/Element} el The container element
35456  * @param {Object} config
35457  */
35458 Roo.tree.ColumnTree =  function(el, config)
35459 {
35460    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35461    this.addEvents({
35462         /**
35463         * @event resize
35464         * Fire this event on a container when it resizes
35465         * @param {int} w Width
35466         * @param {int} h Height
35467         */
35468        "resize" : true
35469     });
35470     this.on('resize', this.onResize, this);
35471 };
35472
35473 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35474     //lines:false,
35475     
35476     
35477     borderWidth: Roo.isBorderBox ? 0 : 2, 
35478     headEls : false,
35479     
35480     render : function(){
35481         // add the header.....
35482        
35483         Roo.tree.ColumnTree.superclass.render.apply(this);
35484         
35485         this.el.addClass('x-column-tree');
35486         
35487         this.headers = this.el.createChild(
35488             {cls:'x-tree-headers'},this.innerCt.dom);
35489    
35490         var cols = this.columns, c;
35491         var totalWidth = 0;
35492         this.headEls = [];
35493         var  len = cols.length;
35494         for(var i = 0; i < len; i++){
35495              c = cols[i];
35496              totalWidth += c.width;
35497             this.headEls.push(this.headers.createChild({
35498                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35499                  cn: {
35500                      cls:'x-tree-hd-text',
35501                      html: c.header
35502                  },
35503                  style:'width:'+(c.width-this.borderWidth)+'px;'
35504              }));
35505         }
35506         this.headers.createChild({cls:'x-clear'});
35507         // prevent floats from wrapping when clipped
35508         this.headers.setWidth(totalWidth);
35509         //this.innerCt.setWidth(totalWidth);
35510         this.innerCt.setStyle({ overflow: 'auto' });
35511         this.onResize(this.width, this.height);
35512              
35513         
35514     },
35515     onResize : function(w,h)
35516     {
35517         this.height = h;
35518         this.width = w;
35519         // resize cols..
35520         this.innerCt.setWidth(this.width);
35521         this.innerCt.setHeight(this.height-20);
35522         
35523         // headers...
35524         var cols = this.columns, c;
35525         var totalWidth = 0;
35526         var expEl = false;
35527         var len = cols.length;
35528         for(var i = 0; i < len; i++){
35529             c = cols[i];
35530             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35531                 // it's the expander..
35532                 expEl  = this.headEls[i];
35533                 continue;
35534             }
35535             totalWidth += c.width;
35536             
35537         }
35538         if (expEl) {
35539             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35540         }
35541         this.headers.setWidth(w-20);
35542
35543         
35544         
35545         
35546     }
35547 });
35548 /*
35549  * Based on:
35550  * Ext JS Library 1.1.1
35551  * Copyright(c) 2006-2007, Ext JS, LLC.
35552  *
35553  * Originally Released Under LGPL - original licence link has changed is not relivant.
35554  *
35555  * Fork - LGPL
35556  * <script type="text/javascript">
35557  */
35558  
35559 /**
35560  * @class Roo.menu.Menu
35561  * @extends Roo.util.Observable
35562  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35563  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35564  * @constructor
35565  * Creates a new Menu
35566  * @param {Object} config Configuration options
35567  */
35568 Roo.menu.Menu = function(config){
35569     Roo.apply(this, config);
35570     this.id = this.id || Roo.id();
35571     this.addEvents({
35572         /**
35573          * @event beforeshow
35574          * Fires before this menu is displayed
35575          * @param {Roo.menu.Menu} this
35576          */
35577         beforeshow : true,
35578         /**
35579          * @event beforehide
35580          * Fires before this menu is hidden
35581          * @param {Roo.menu.Menu} this
35582          */
35583         beforehide : true,
35584         /**
35585          * @event show
35586          * Fires after this menu is displayed
35587          * @param {Roo.menu.Menu} this
35588          */
35589         show : true,
35590         /**
35591          * @event hide
35592          * Fires after this menu is hidden
35593          * @param {Roo.menu.Menu} this
35594          */
35595         hide : true,
35596         /**
35597          * @event click
35598          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35599          * @param {Roo.menu.Menu} this
35600          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35601          * @param {Roo.EventObject} e
35602          */
35603         click : true,
35604         /**
35605          * @event mouseover
35606          * Fires when the mouse is hovering over this menu
35607          * @param {Roo.menu.Menu} this
35608          * @param {Roo.EventObject} e
35609          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35610          */
35611         mouseover : true,
35612         /**
35613          * @event mouseout
35614          * Fires when the mouse exits this menu
35615          * @param {Roo.menu.Menu} this
35616          * @param {Roo.EventObject} e
35617          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35618          */
35619         mouseout : true,
35620         /**
35621          * @event itemclick
35622          * Fires when a menu item contained in this menu is clicked
35623          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35624          * @param {Roo.EventObject} e
35625          */
35626         itemclick: true
35627     });
35628     if (this.registerMenu) {
35629         Roo.menu.MenuMgr.register(this);
35630     }
35631     
35632     var mis = this.items;
35633     this.items = new Roo.util.MixedCollection();
35634     if(mis){
35635         this.add.apply(this, mis);
35636     }
35637 };
35638
35639 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35640     /**
35641      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35642      */
35643     minWidth : 120,
35644     /**
35645      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35646      * for bottom-right shadow (defaults to "sides")
35647      */
35648     shadow : "sides",
35649     /**
35650      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35651      * this menu (defaults to "tl-tr?")
35652      */
35653     subMenuAlign : "tl-tr?",
35654     /**
35655      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35656      * relative to its element of origin (defaults to "tl-bl?")
35657      */
35658     defaultAlign : "tl-bl?",
35659     /**
35660      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35661      */
35662     allowOtherMenus : false,
35663     /**
35664      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35665      */
35666     registerMenu : true,
35667
35668     hidden:true,
35669
35670     // private
35671     render : function(){
35672         if(this.el){
35673             return;
35674         }
35675         var el = this.el = new Roo.Layer({
35676             cls: "x-menu",
35677             shadow:this.shadow,
35678             constrain: false,
35679             parentEl: this.parentEl || document.body,
35680             zindex:15000
35681         });
35682
35683         this.keyNav = new Roo.menu.MenuNav(this);
35684
35685         if(this.plain){
35686             el.addClass("x-menu-plain");
35687         }
35688         if(this.cls){
35689             el.addClass(this.cls);
35690         }
35691         // generic focus element
35692         this.focusEl = el.createChild({
35693             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35694         });
35695         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35696         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35697         
35698         ul.on("mouseover", this.onMouseOver, this);
35699         ul.on("mouseout", this.onMouseOut, this);
35700         this.items.each(function(item){
35701             if (item.hidden) {
35702                 return;
35703             }
35704             
35705             var li = document.createElement("li");
35706             li.className = "x-menu-list-item";
35707             ul.dom.appendChild(li);
35708             item.render(li, this);
35709         }, this);
35710         this.ul = ul;
35711         this.autoWidth();
35712     },
35713
35714     // private
35715     autoWidth : function(){
35716         var el = this.el, ul = this.ul;
35717         if(!el){
35718             return;
35719         }
35720         var w = this.width;
35721         if(w){
35722             el.setWidth(w);
35723         }else if(Roo.isIE){
35724             el.setWidth(this.minWidth);
35725             var t = el.dom.offsetWidth; // force recalc
35726             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35727         }
35728     },
35729
35730     // private
35731     delayAutoWidth : function(){
35732         if(this.rendered){
35733             if(!this.awTask){
35734                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35735             }
35736             this.awTask.delay(20);
35737         }
35738     },
35739
35740     // private
35741     findTargetItem : function(e){
35742         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35743         if(t && t.menuItemId){
35744             return this.items.get(t.menuItemId);
35745         }
35746     },
35747
35748     // private
35749     onClick : function(e){
35750         Roo.log("menu.onClick");
35751         var t = this.findTargetItem(e);
35752         if(!t){
35753             return;
35754         }
35755         Roo.log(e);
35756         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35757             if(t == this.activeItem && t.shouldDeactivate(e)){
35758                 this.activeItem.deactivate();
35759                 delete this.activeItem;
35760                 return;
35761             }
35762             if(t.canActivate){
35763                 this.setActiveItem(t, true);
35764             }
35765             return;
35766             
35767             
35768         }
35769         
35770         t.onClick(e);
35771         this.fireEvent("click", this, t, e);
35772     },
35773
35774     // private
35775     setActiveItem : function(item, autoExpand){
35776         if(item != this.activeItem){
35777             if(this.activeItem){
35778                 this.activeItem.deactivate();
35779             }
35780             this.activeItem = item;
35781             item.activate(autoExpand);
35782         }else if(autoExpand){
35783             item.expandMenu();
35784         }
35785     },
35786
35787     // private
35788     tryActivate : function(start, step){
35789         var items = this.items;
35790         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35791             var item = items.get(i);
35792             if(!item.disabled && item.canActivate){
35793                 this.setActiveItem(item, false);
35794                 return item;
35795             }
35796         }
35797         return false;
35798     },
35799
35800     // private
35801     onMouseOver : function(e){
35802         var t;
35803         if(t = this.findTargetItem(e)){
35804             if(t.canActivate && !t.disabled){
35805                 this.setActiveItem(t, true);
35806             }
35807         }
35808         this.fireEvent("mouseover", this, e, t);
35809     },
35810
35811     // private
35812     onMouseOut : function(e){
35813         var t;
35814         if(t = this.findTargetItem(e)){
35815             if(t == this.activeItem && t.shouldDeactivate(e)){
35816                 this.activeItem.deactivate();
35817                 delete this.activeItem;
35818             }
35819         }
35820         this.fireEvent("mouseout", this, e, t);
35821     },
35822
35823     /**
35824      * Read-only.  Returns true if the menu is currently displayed, else false.
35825      * @type Boolean
35826      */
35827     isVisible : function(){
35828         return this.el && !this.hidden;
35829     },
35830
35831     /**
35832      * Displays this menu relative to another element
35833      * @param {String/HTMLElement/Roo.Element} element The element to align to
35834      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35835      * the element (defaults to this.defaultAlign)
35836      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35837      */
35838     show : function(el, pos, parentMenu){
35839         this.parentMenu = parentMenu;
35840         if(!this.el){
35841             this.render();
35842         }
35843         this.fireEvent("beforeshow", this);
35844         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35845     },
35846
35847     /**
35848      * Displays this menu at a specific xy position
35849      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35850      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35851      */
35852     showAt : function(xy, parentMenu, /* private: */_e){
35853         this.parentMenu = parentMenu;
35854         if(!this.el){
35855             this.render();
35856         }
35857         if(_e !== false){
35858             this.fireEvent("beforeshow", this);
35859             xy = this.el.adjustForConstraints(xy);
35860         }
35861         this.el.setXY(xy);
35862         this.el.show();
35863         this.hidden = false;
35864         this.focus();
35865         this.fireEvent("show", this);
35866     },
35867
35868     focus : function(){
35869         if(!this.hidden){
35870             this.doFocus.defer(50, this);
35871         }
35872     },
35873
35874     doFocus : function(){
35875         if(!this.hidden){
35876             this.focusEl.focus();
35877         }
35878     },
35879
35880     /**
35881      * Hides this menu and optionally all parent menus
35882      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35883      */
35884     hide : function(deep){
35885         if(this.el && this.isVisible()){
35886             this.fireEvent("beforehide", this);
35887             if(this.activeItem){
35888                 this.activeItem.deactivate();
35889                 this.activeItem = null;
35890             }
35891             this.el.hide();
35892             this.hidden = true;
35893             this.fireEvent("hide", this);
35894         }
35895         if(deep === true && this.parentMenu){
35896             this.parentMenu.hide(true);
35897         }
35898     },
35899
35900     /**
35901      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35902      * Any of the following are valid:
35903      * <ul>
35904      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35905      * <li>An HTMLElement object which will be converted to a menu item</li>
35906      * <li>A menu item config object that will be created as a new menu item</li>
35907      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35908      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35909      * </ul>
35910      * Usage:
35911      * <pre><code>
35912 // Create the menu
35913 var menu = new Roo.menu.Menu();
35914
35915 // Create a menu item to add by reference
35916 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35917
35918 // Add a bunch of items at once using different methods.
35919 // Only the last item added will be returned.
35920 var item = menu.add(
35921     menuItem,                // add existing item by ref
35922     'Dynamic Item',          // new TextItem
35923     '-',                     // new separator
35924     { text: 'Config Item' }  // new item by config
35925 );
35926 </code></pre>
35927      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35928      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35929      */
35930     add : function(){
35931         var a = arguments, l = a.length, item;
35932         for(var i = 0; i < l; i++){
35933             var el = a[i];
35934             if ((typeof(el) == "object") && el.xtype && el.xns) {
35935                 el = Roo.factory(el, Roo.menu);
35936             }
35937             
35938             if(el.render){ // some kind of Item
35939                 item = this.addItem(el);
35940             }else if(typeof el == "string"){ // string
35941                 if(el == "separator" || el == "-"){
35942                     item = this.addSeparator();
35943                 }else{
35944                     item = this.addText(el);
35945                 }
35946             }else if(el.tagName || el.el){ // element
35947                 item = this.addElement(el);
35948             }else if(typeof el == "object"){ // must be menu item config?
35949                 item = this.addMenuItem(el);
35950             }
35951         }
35952         return item;
35953     },
35954
35955     /**
35956      * Returns this menu's underlying {@link Roo.Element} object
35957      * @return {Roo.Element} The element
35958      */
35959     getEl : function(){
35960         if(!this.el){
35961             this.render();
35962         }
35963         return this.el;
35964     },
35965
35966     /**
35967      * Adds a separator bar to the menu
35968      * @return {Roo.menu.Item} The menu item that was added
35969      */
35970     addSeparator : function(){
35971         return this.addItem(new Roo.menu.Separator());
35972     },
35973
35974     /**
35975      * Adds an {@link Roo.Element} object to the menu
35976      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35977      * @return {Roo.menu.Item} The menu item that was added
35978      */
35979     addElement : function(el){
35980         return this.addItem(new Roo.menu.BaseItem(el));
35981     },
35982
35983     /**
35984      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35985      * @param {Roo.menu.Item} item The menu item to add
35986      * @return {Roo.menu.Item} The menu item that was added
35987      */
35988     addItem : function(item){
35989         this.items.add(item);
35990         if(this.ul){
35991             var li = document.createElement("li");
35992             li.className = "x-menu-list-item";
35993             this.ul.dom.appendChild(li);
35994             item.render(li, this);
35995             this.delayAutoWidth();
35996         }
35997         return item;
35998     },
35999
36000     /**
36001      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
36002      * @param {Object} config A MenuItem config object
36003      * @return {Roo.menu.Item} The menu item that was added
36004      */
36005     addMenuItem : function(config){
36006         if(!(config instanceof Roo.menu.Item)){
36007             if(typeof config.checked == "boolean"){ // must be check menu item config?
36008                 config = new Roo.menu.CheckItem(config);
36009             }else{
36010                 config = new Roo.menu.Item(config);
36011             }
36012         }
36013         return this.addItem(config);
36014     },
36015
36016     /**
36017      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36018      * @param {String} text The text to display in the menu item
36019      * @return {Roo.menu.Item} The menu item that was added
36020      */
36021     addText : function(text){
36022         return this.addItem(new Roo.menu.TextItem({ text : text }));
36023     },
36024
36025     /**
36026      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36027      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36028      * @param {Roo.menu.Item} item The menu item to add
36029      * @return {Roo.menu.Item} The menu item that was added
36030      */
36031     insert : function(index, item){
36032         this.items.insert(index, item);
36033         if(this.ul){
36034             var li = document.createElement("li");
36035             li.className = "x-menu-list-item";
36036             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36037             item.render(li, this);
36038             this.delayAutoWidth();
36039         }
36040         return item;
36041     },
36042
36043     /**
36044      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36045      * @param {Roo.menu.Item} item The menu item to remove
36046      */
36047     remove : function(item){
36048         this.items.removeKey(item.id);
36049         item.destroy();
36050     },
36051
36052     /**
36053      * Removes and destroys all items in the menu
36054      */
36055     removeAll : function(){
36056         var f;
36057         while(f = this.items.first()){
36058             this.remove(f);
36059         }
36060     }
36061 });
36062
36063 // MenuNav is a private utility class used internally by the Menu
36064 Roo.menu.MenuNav = function(menu){
36065     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36066     this.scope = this.menu = menu;
36067 };
36068
36069 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36070     doRelay : function(e, h){
36071         var k = e.getKey();
36072         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36073             this.menu.tryActivate(0, 1);
36074             return false;
36075         }
36076         return h.call(this.scope || this, e, this.menu);
36077     },
36078
36079     up : function(e, m){
36080         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36081             m.tryActivate(m.items.length-1, -1);
36082         }
36083     },
36084
36085     down : function(e, m){
36086         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36087             m.tryActivate(0, 1);
36088         }
36089     },
36090
36091     right : function(e, m){
36092         if(m.activeItem){
36093             m.activeItem.expandMenu(true);
36094         }
36095     },
36096
36097     left : function(e, m){
36098         m.hide();
36099         if(m.parentMenu && m.parentMenu.activeItem){
36100             m.parentMenu.activeItem.activate();
36101         }
36102     },
36103
36104     enter : function(e, m){
36105         if(m.activeItem){
36106             e.stopPropagation();
36107             m.activeItem.onClick(e);
36108             m.fireEvent("click", this, m.activeItem);
36109             return true;
36110         }
36111     }
36112 });/*
36113  * Based on:
36114  * Ext JS Library 1.1.1
36115  * Copyright(c) 2006-2007, Ext JS, LLC.
36116  *
36117  * Originally Released Under LGPL - original licence link has changed is not relivant.
36118  *
36119  * Fork - LGPL
36120  * <script type="text/javascript">
36121  */
36122  
36123 /**
36124  * @class Roo.menu.MenuMgr
36125  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36126  * @singleton
36127  */
36128 Roo.menu.MenuMgr = function(){
36129    var menus, active, groups = {}, attached = false, lastShow = new Date();
36130
36131    // private - called when first menu is created
36132    function init(){
36133        menus = {};
36134        active = new Roo.util.MixedCollection();
36135        Roo.get(document).addKeyListener(27, function(){
36136            if(active.length > 0){
36137                hideAll();
36138            }
36139        });
36140    }
36141
36142    // private
36143    function hideAll(){
36144        if(active && active.length > 0){
36145            var c = active.clone();
36146            c.each(function(m){
36147                m.hide();
36148            });
36149        }
36150    }
36151
36152    // private
36153    function onHide(m){
36154        active.remove(m);
36155        if(active.length < 1){
36156            Roo.get(document).un("mousedown", onMouseDown);
36157            attached = false;
36158        }
36159    }
36160
36161    // private
36162    function onShow(m){
36163        var last = active.last();
36164        lastShow = new Date();
36165        active.add(m);
36166        if(!attached){
36167            Roo.get(document).on("mousedown", onMouseDown);
36168            attached = true;
36169        }
36170        if(m.parentMenu){
36171           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36172           m.parentMenu.activeChild = m;
36173        }else if(last && last.isVisible()){
36174           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36175        }
36176    }
36177
36178    // private
36179    function onBeforeHide(m){
36180        if(m.activeChild){
36181            m.activeChild.hide();
36182        }
36183        if(m.autoHideTimer){
36184            clearTimeout(m.autoHideTimer);
36185            delete m.autoHideTimer;
36186        }
36187    }
36188
36189    // private
36190    function onBeforeShow(m){
36191        var pm = m.parentMenu;
36192        if(!pm && !m.allowOtherMenus){
36193            hideAll();
36194        }else if(pm && pm.activeChild && active != m){
36195            pm.activeChild.hide();
36196        }
36197    }
36198
36199    // private
36200    function onMouseDown(e){
36201        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36202            hideAll();
36203        }
36204    }
36205
36206    // private
36207    function onBeforeCheck(mi, state){
36208        if(state){
36209            var g = groups[mi.group];
36210            for(var i = 0, l = g.length; i < l; i++){
36211                if(g[i] != mi){
36212                    g[i].setChecked(false);
36213                }
36214            }
36215        }
36216    }
36217
36218    return {
36219
36220        /**
36221         * Hides all menus that are currently visible
36222         */
36223        hideAll : function(){
36224             hideAll();  
36225        },
36226
36227        // private
36228        register : function(menu){
36229            if(!menus){
36230                init();
36231            }
36232            menus[menu.id] = menu;
36233            menu.on("beforehide", onBeforeHide);
36234            menu.on("hide", onHide);
36235            menu.on("beforeshow", onBeforeShow);
36236            menu.on("show", onShow);
36237            var g = menu.group;
36238            if(g && menu.events["checkchange"]){
36239                if(!groups[g]){
36240                    groups[g] = [];
36241                }
36242                groups[g].push(menu);
36243                menu.on("checkchange", onCheck);
36244            }
36245        },
36246
36247         /**
36248          * Returns a {@link Roo.menu.Menu} object
36249          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36250          * be used to generate and return a new Menu instance.
36251          */
36252        get : function(menu){
36253            if(typeof menu == "string"){ // menu id
36254                return menus[menu];
36255            }else if(menu.events){  // menu instance
36256                return menu;
36257            }else if(typeof menu.length == 'number'){ // array of menu items?
36258                return new Roo.menu.Menu({items:menu});
36259            }else{ // otherwise, must be a config
36260                return new Roo.menu.Menu(menu);
36261            }
36262        },
36263
36264        // private
36265        unregister : function(menu){
36266            delete menus[menu.id];
36267            menu.un("beforehide", onBeforeHide);
36268            menu.un("hide", onHide);
36269            menu.un("beforeshow", onBeforeShow);
36270            menu.un("show", onShow);
36271            var g = menu.group;
36272            if(g && menu.events["checkchange"]){
36273                groups[g].remove(menu);
36274                menu.un("checkchange", onCheck);
36275            }
36276        },
36277
36278        // private
36279        registerCheckable : function(menuItem){
36280            var g = menuItem.group;
36281            if(g){
36282                if(!groups[g]){
36283                    groups[g] = [];
36284                }
36285                groups[g].push(menuItem);
36286                menuItem.on("beforecheckchange", onBeforeCheck);
36287            }
36288        },
36289
36290        // private
36291        unregisterCheckable : function(menuItem){
36292            var g = menuItem.group;
36293            if(g){
36294                groups[g].remove(menuItem);
36295                menuItem.un("beforecheckchange", onBeforeCheck);
36296            }
36297        }
36298    };
36299 }();/*
36300  * Based on:
36301  * Ext JS Library 1.1.1
36302  * Copyright(c) 2006-2007, Ext JS, LLC.
36303  *
36304  * Originally Released Under LGPL - original licence link has changed is not relivant.
36305  *
36306  * Fork - LGPL
36307  * <script type="text/javascript">
36308  */
36309  
36310
36311 /**
36312  * @class Roo.menu.BaseItem
36313  * @extends Roo.Component
36314  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36315  * management and base configuration options shared by all menu components.
36316  * @constructor
36317  * Creates a new BaseItem
36318  * @param {Object} config Configuration options
36319  */
36320 Roo.menu.BaseItem = function(config){
36321     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36322
36323     this.addEvents({
36324         /**
36325          * @event click
36326          * Fires when this item is clicked
36327          * @param {Roo.menu.BaseItem} this
36328          * @param {Roo.EventObject} e
36329          */
36330         click: true,
36331         /**
36332          * @event activate
36333          * Fires when this item is activated
36334          * @param {Roo.menu.BaseItem} this
36335          */
36336         activate : true,
36337         /**
36338          * @event deactivate
36339          * Fires when this item is deactivated
36340          * @param {Roo.menu.BaseItem} this
36341          */
36342         deactivate : true
36343     });
36344
36345     if(this.handler){
36346         this.on("click", this.handler, this.scope, true);
36347     }
36348 };
36349
36350 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36351     /**
36352      * @cfg {Function} handler
36353      * A function that will handle the click event of this menu item (defaults to undefined)
36354      */
36355     /**
36356      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36357      */
36358     canActivate : false,
36359     
36360      /**
36361      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36362      */
36363     hidden: false,
36364     
36365     /**
36366      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36367      */
36368     activeClass : "x-menu-item-active",
36369     /**
36370      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36371      */
36372     hideOnClick : true,
36373     /**
36374      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36375      */
36376     hideDelay : 100,
36377
36378     // private
36379     ctype: "Roo.menu.BaseItem",
36380
36381     // private
36382     actionMode : "container",
36383
36384     // private
36385     render : function(container, parentMenu){
36386         this.parentMenu = parentMenu;
36387         Roo.menu.BaseItem.superclass.render.call(this, container);
36388         this.container.menuItemId = this.id;
36389     },
36390
36391     // private
36392     onRender : function(container, position){
36393         this.el = Roo.get(this.el);
36394         container.dom.appendChild(this.el.dom);
36395     },
36396
36397     // private
36398     onClick : function(e){
36399         if(!this.disabled && this.fireEvent("click", this, e) !== false
36400                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36401             this.handleClick(e);
36402         }else{
36403             e.stopEvent();
36404         }
36405     },
36406
36407     // private
36408     activate : function(){
36409         if(this.disabled){
36410             return false;
36411         }
36412         var li = this.container;
36413         li.addClass(this.activeClass);
36414         this.region = li.getRegion().adjust(2, 2, -2, -2);
36415         this.fireEvent("activate", this);
36416         return true;
36417     },
36418
36419     // private
36420     deactivate : function(){
36421         this.container.removeClass(this.activeClass);
36422         this.fireEvent("deactivate", this);
36423     },
36424
36425     // private
36426     shouldDeactivate : function(e){
36427         return !this.region || !this.region.contains(e.getPoint());
36428     },
36429
36430     // private
36431     handleClick : function(e){
36432         if(this.hideOnClick){
36433             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36434         }
36435     },
36436
36437     // private
36438     expandMenu : function(autoActivate){
36439         // do nothing
36440     },
36441
36442     // private
36443     hideMenu : function(){
36444         // do nothing
36445     }
36446 });/*
36447  * Based on:
36448  * Ext JS Library 1.1.1
36449  * Copyright(c) 2006-2007, Ext JS, LLC.
36450  *
36451  * Originally Released Under LGPL - original licence link has changed is not relivant.
36452  *
36453  * Fork - LGPL
36454  * <script type="text/javascript">
36455  */
36456  
36457 /**
36458  * @class Roo.menu.Adapter
36459  * @extends Roo.menu.BaseItem
36460  * 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.
36461  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36462  * @constructor
36463  * Creates a new Adapter
36464  * @param {Object} config Configuration options
36465  */
36466 Roo.menu.Adapter = function(component, config){
36467     Roo.menu.Adapter.superclass.constructor.call(this, config);
36468     this.component = component;
36469 };
36470 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36471     // private
36472     canActivate : true,
36473
36474     // private
36475     onRender : function(container, position){
36476         this.component.render(container);
36477         this.el = this.component.getEl();
36478     },
36479
36480     // private
36481     activate : function(){
36482         if(this.disabled){
36483             return false;
36484         }
36485         this.component.focus();
36486         this.fireEvent("activate", this);
36487         return true;
36488     },
36489
36490     // private
36491     deactivate : function(){
36492         this.fireEvent("deactivate", this);
36493     },
36494
36495     // private
36496     disable : function(){
36497         this.component.disable();
36498         Roo.menu.Adapter.superclass.disable.call(this);
36499     },
36500
36501     // private
36502     enable : function(){
36503         this.component.enable();
36504         Roo.menu.Adapter.superclass.enable.call(this);
36505     }
36506 });/*
36507  * Based on:
36508  * Ext JS Library 1.1.1
36509  * Copyright(c) 2006-2007, Ext JS, LLC.
36510  *
36511  * Originally Released Under LGPL - original licence link has changed is not relivant.
36512  *
36513  * Fork - LGPL
36514  * <script type="text/javascript">
36515  */
36516
36517 /**
36518  * @class Roo.menu.TextItem
36519  * @extends Roo.menu.BaseItem
36520  * Adds a static text string to a menu, usually used as either a heading or group separator.
36521  * Note: old style constructor with text is still supported.
36522  * 
36523  * @constructor
36524  * Creates a new TextItem
36525  * @param {Object} cfg Configuration
36526  */
36527 Roo.menu.TextItem = function(cfg){
36528     if (typeof(cfg) == 'string') {
36529         this.text = cfg;
36530     } else {
36531         Roo.apply(this,cfg);
36532     }
36533     
36534     Roo.menu.TextItem.superclass.constructor.call(this);
36535 };
36536
36537 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36538     /**
36539      * @cfg {Boolean} text Text to show on item.
36540      */
36541     text : '',
36542     
36543     /**
36544      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36545      */
36546     hideOnClick : false,
36547     /**
36548      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36549      */
36550     itemCls : "x-menu-text",
36551
36552     // private
36553     onRender : function(){
36554         var s = document.createElement("span");
36555         s.className = this.itemCls;
36556         s.innerHTML = this.text;
36557         this.el = s;
36558         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36559     }
36560 });/*
36561  * Based on:
36562  * Ext JS Library 1.1.1
36563  * Copyright(c) 2006-2007, Ext JS, LLC.
36564  *
36565  * Originally Released Under LGPL - original licence link has changed is not relivant.
36566  *
36567  * Fork - LGPL
36568  * <script type="text/javascript">
36569  */
36570
36571 /**
36572  * @class Roo.menu.Separator
36573  * @extends Roo.menu.BaseItem
36574  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36575  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36576  * @constructor
36577  * @param {Object} config Configuration options
36578  */
36579 Roo.menu.Separator = function(config){
36580     Roo.menu.Separator.superclass.constructor.call(this, config);
36581 };
36582
36583 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36584     /**
36585      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36586      */
36587     itemCls : "x-menu-sep",
36588     /**
36589      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36590      */
36591     hideOnClick : false,
36592
36593     // private
36594     onRender : function(li){
36595         var s = document.createElement("span");
36596         s.className = this.itemCls;
36597         s.innerHTML = "&#160;";
36598         this.el = s;
36599         li.addClass("x-menu-sep-li");
36600         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36601     }
36602 });/*
36603  * Based on:
36604  * Ext JS Library 1.1.1
36605  * Copyright(c) 2006-2007, Ext JS, LLC.
36606  *
36607  * Originally Released Under LGPL - original licence link has changed is not relivant.
36608  *
36609  * Fork - LGPL
36610  * <script type="text/javascript">
36611  */
36612 /**
36613  * @class Roo.menu.Item
36614  * @extends Roo.menu.BaseItem
36615  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36616  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36617  * activation and click handling.
36618  * @constructor
36619  * Creates a new Item
36620  * @param {Object} config Configuration options
36621  */
36622 Roo.menu.Item = function(config){
36623     Roo.menu.Item.superclass.constructor.call(this, config);
36624     if(this.menu){
36625         this.menu = Roo.menu.MenuMgr.get(this.menu);
36626     }
36627 };
36628 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36629     
36630     /**
36631      * @cfg {String} text
36632      * The text to show on the menu item.
36633      */
36634     text: '',
36635      /**
36636      * @cfg {String} HTML to render in menu
36637      * The text to show on the menu item (HTML version).
36638      */
36639     html: '',
36640     /**
36641      * @cfg {String} icon
36642      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36643      */
36644     icon: undefined,
36645     /**
36646      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36647      */
36648     itemCls : "x-menu-item",
36649     /**
36650      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36651      */
36652     canActivate : true,
36653     /**
36654      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36655      */
36656     showDelay: 200,
36657     // doc'd in BaseItem
36658     hideDelay: 200,
36659
36660     // private
36661     ctype: "Roo.menu.Item",
36662     
36663     // private
36664     onRender : function(container, position){
36665         var el = document.createElement("a");
36666         el.hideFocus = true;
36667         el.unselectable = "on";
36668         el.href = this.href || "#";
36669         if(this.hrefTarget){
36670             el.target = this.hrefTarget;
36671         }
36672         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36673         
36674         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36675         
36676         el.innerHTML = String.format(
36677                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36678                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36679         this.el = el;
36680         Roo.menu.Item.superclass.onRender.call(this, container, position);
36681     },
36682
36683     /**
36684      * Sets the text to display in this menu item
36685      * @param {String} text The text to display
36686      * @param {Boolean} isHTML true to indicate text is pure html.
36687      */
36688     setText : function(text, isHTML){
36689         if (isHTML) {
36690             this.html = text;
36691         } else {
36692             this.text = text;
36693             this.html = '';
36694         }
36695         if(this.rendered){
36696             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36697      
36698             this.el.update(String.format(
36699                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36700                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36701             this.parentMenu.autoWidth();
36702         }
36703     },
36704
36705     // private
36706     handleClick : function(e){
36707         if(!this.href){ // if no link defined, stop the event automatically
36708             e.stopEvent();
36709         }
36710         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36711     },
36712
36713     // private
36714     activate : function(autoExpand){
36715         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36716             this.focus();
36717             if(autoExpand){
36718                 this.expandMenu();
36719             }
36720         }
36721         return true;
36722     },
36723
36724     // private
36725     shouldDeactivate : function(e){
36726         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36727             if(this.menu && this.menu.isVisible()){
36728                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36729             }
36730             return true;
36731         }
36732         return false;
36733     },
36734
36735     // private
36736     deactivate : function(){
36737         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36738         this.hideMenu();
36739     },
36740
36741     // private
36742     expandMenu : function(autoActivate){
36743         if(!this.disabled && this.menu){
36744             clearTimeout(this.hideTimer);
36745             delete this.hideTimer;
36746             if(!this.menu.isVisible() && !this.showTimer){
36747                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36748             }else if (this.menu.isVisible() && autoActivate){
36749                 this.menu.tryActivate(0, 1);
36750             }
36751         }
36752     },
36753
36754     // private
36755     deferExpand : function(autoActivate){
36756         delete this.showTimer;
36757         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36758         if(autoActivate){
36759             this.menu.tryActivate(0, 1);
36760         }
36761     },
36762
36763     // private
36764     hideMenu : function(){
36765         clearTimeout(this.showTimer);
36766         delete this.showTimer;
36767         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36768             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36769         }
36770     },
36771
36772     // private
36773     deferHide : function(){
36774         delete this.hideTimer;
36775         this.menu.hide();
36776     }
36777 });/*
36778  * Based on:
36779  * Ext JS Library 1.1.1
36780  * Copyright(c) 2006-2007, Ext JS, LLC.
36781  *
36782  * Originally Released Under LGPL - original licence link has changed is not relivant.
36783  *
36784  * Fork - LGPL
36785  * <script type="text/javascript">
36786  */
36787  
36788 /**
36789  * @class Roo.menu.CheckItem
36790  * @extends Roo.menu.Item
36791  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36792  * @constructor
36793  * Creates a new CheckItem
36794  * @param {Object} config Configuration options
36795  */
36796 Roo.menu.CheckItem = function(config){
36797     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36798     this.addEvents({
36799         /**
36800          * @event beforecheckchange
36801          * Fires before the checked value is set, providing an opportunity to cancel if needed
36802          * @param {Roo.menu.CheckItem} this
36803          * @param {Boolean} checked The new checked value that will be set
36804          */
36805         "beforecheckchange" : true,
36806         /**
36807          * @event checkchange
36808          * Fires after the checked value has been set
36809          * @param {Roo.menu.CheckItem} this
36810          * @param {Boolean} checked The checked value that was set
36811          */
36812         "checkchange" : true
36813     });
36814     if(this.checkHandler){
36815         this.on('checkchange', this.checkHandler, this.scope);
36816     }
36817 };
36818 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36819     /**
36820      * @cfg {String} group
36821      * All check items with the same group name will automatically be grouped into a single-select
36822      * radio button group (defaults to '')
36823      */
36824     /**
36825      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36826      */
36827     itemCls : "x-menu-item x-menu-check-item",
36828     /**
36829      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36830      */
36831     groupClass : "x-menu-group-item",
36832
36833     /**
36834      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36835      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36836      * initialized with checked = true will be rendered as checked.
36837      */
36838     checked: false,
36839
36840     // private
36841     ctype: "Roo.menu.CheckItem",
36842
36843     // private
36844     onRender : function(c){
36845         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36846         if(this.group){
36847             this.el.addClass(this.groupClass);
36848         }
36849         Roo.menu.MenuMgr.registerCheckable(this);
36850         if(this.checked){
36851             this.checked = false;
36852             this.setChecked(true, true);
36853         }
36854     },
36855
36856     // private
36857     destroy : function(){
36858         if(this.rendered){
36859             Roo.menu.MenuMgr.unregisterCheckable(this);
36860         }
36861         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36862     },
36863
36864     /**
36865      * Set the checked state of this item
36866      * @param {Boolean} checked The new checked value
36867      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36868      */
36869     setChecked : function(state, suppressEvent){
36870         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36871             if(this.container){
36872                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36873             }
36874             this.checked = state;
36875             if(suppressEvent !== true){
36876                 this.fireEvent("checkchange", this, state);
36877             }
36878         }
36879     },
36880
36881     // private
36882     handleClick : function(e){
36883        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36884            this.setChecked(!this.checked);
36885        }
36886        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36887     }
36888 });/*
36889  * Based on:
36890  * Ext JS Library 1.1.1
36891  * Copyright(c) 2006-2007, Ext JS, LLC.
36892  *
36893  * Originally Released Under LGPL - original licence link has changed is not relivant.
36894  *
36895  * Fork - LGPL
36896  * <script type="text/javascript">
36897  */
36898  
36899 /**
36900  * @class Roo.menu.DateItem
36901  * @extends Roo.menu.Adapter
36902  * A menu item that wraps the {@link Roo.DatPicker} component.
36903  * @constructor
36904  * Creates a new DateItem
36905  * @param {Object} config Configuration options
36906  */
36907 Roo.menu.DateItem = function(config){
36908     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36909     /** The Roo.DatePicker object @type Roo.DatePicker */
36910     this.picker = this.component;
36911     this.addEvents({select: true});
36912     
36913     this.picker.on("render", function(picker){
36914         picker.getEl().swallowEvent("click");
36915         picker.container.addClass("x-menu-date-item");
36916     });
36917
36918     this.picker.on("select", this.onSelect, this);
36919 };
36920
36921 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36922     // private
36923     onSelect : function(picker, date){
36924         this.fireEvent("select", this, date, picker);
36925         Roo.menu.DateItem.superclass.handleClick.call(this);
36926     }
36927 });/*
36928  * Based on:
36929  * Ext JS Library 1.1.1
36930  * Copyright(c) 2006-2007, Ext JS, LLC.
36931  *
36932  * Originally Released Under LGPL - original licence link has changed is not relivant.
36933  *
36934  * Fork - LGPL
36935  * <script type="text/javascript">
36936  */
36937  
36938 /**
36939  * @class Roo.menu.ColorItem
36940  * @extends Roo.menu.Adapter
36941  * A menu item that wraps the {@link Roo.ColorPalette} component.
36942  * @constructor
36943  * Creates a new ColorItem
36944  * @param {Object} config Configuration options
36945  */
36946 Roo.menu.ColorItem = function(config){
36947     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36948     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36949     this.palette = this.component;
36950     this.relayEvents(this.palette, ["select"]);
36951     if(this.selectHandler){
36952         this.on('select', this.selectHandler, this.scope);
36953     }
36954 };
36955 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36956  * Based on:
36957  * Ext JS Library 1.1.1
36958  * Copyright(c) 2006-2007, Ext JS, LLC.
36959  *
36960  * Originally Released Under LGPL - original licence link has changed is not relivant.
36961  *
36962  * Fork - LGPL
36963  * <script type="text/javascript">
36964  */
36965  
36966
36967 /**
36968  * @class Roo.menu.DateMenu
36969  * @extends Roo.menu.Menu
36970  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36971  * @constructor
36972  * Creates a new DateMenu
36973  * @param {Object} config Configuration options
36974  */
36975 Roo.menu.DateMenu = function(config){
36976     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36977     this.plain = true;
36978     var di = new Roo.menu.DateItem(config);
36979     this.add(di);
36980     /**
36981      * The {@link Roo.DatePicker} instance for this DateMenu
36982      * @type DatePicker
36983      */
36984     this.picker = di.picker;
36985     /**
36986      * @event select
36987      * @param {DatePicker} picker
36988      * @param {Date} date
36989      */
36990     this.relayEvents(di, ["select"]);
36991     this.on('beforeshow', function(){
36992         if(this.picker){
36993             this.picker.hideMonthPicker(false);
36994         }
36995     }, this);
36996 };
36997 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36998     cls:'x-date-menu'
36999 });/*
37000  * Based on:
37001  * Ext JS Library 1.1.1
37002  * Copyright(c) 2006-2007, Ext JS, LLC.
37003  *
37004  * Originally Released Under LGPL - original licence link has changed is not relivant.
37005  *
37006  * Fork - LGPL
37007  * <script type="text/javascript">
37008  */
37009  
37010
37011 /**
37012  * @class Roo.menu.ColorMenu
37013  * @extends Roo.menu.Menu
37014  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37015  * @constructor
37016  * Creates a new ColorMenu
37017  * @param {Object} config Configuration options
37018  */
37019 Roo.menu.ColorMenu = function(config){
37020     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37021     this.plain = true;
37022     var ci = new Roo.menu.ColorItem(config);
37023     this.add(ci);
37024     /**
37025      * The {@link Roo.ColorPalette} instance for this ColorMenu
37026      * @type ColorPalette
37027      */
37028     this.palette = ci.palette;
37029     /**
37030      * @event select
37031      * @param {ColorPalette} palette
37032      * @param {String} color
37033      */
37034     this.relayEvents(ci, ["select"]);
37035 };
37036 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37037  * Based on:
37038  * Ext JS Library 1.1.1
37039  * Copyright(c) 2006-2007, Ext JS, LLC.
37040  *
37041  * Originally Released Under LGPL - original licence link has changed is not relivant.
37042  *
37043  * Fork - LGPL
37044  * <script type="text/javascript">
37045  */
37046  
37047 /**
37048  * @class Roo.form.Field
37049  * @extends Roo.BoxComponent
37050  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37051  * @constructor
37052  * Creates a new Field
37053  * @param {Object} config Configuration options
37054  */
37055 Roo.form.Field = function(config){
37056     Roo.form.Field.superclass.constructor.call(this, config);
37057 };
37058
37059 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37060     /**
37061      * @cfg {String} fieldLabel Label to use when rendering a form.
37062      */
37063        /**
37064      * @cfg {String} qtip Mouse over tip
37065      */
37066      
37067     /**
37068      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37069      */
37070     invalidClass : "x-form-invalid",
37071     /**
37072      * @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")
37073      */
37074     invalidText : "The value in this field is invalid",
37075     /**
37076      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37077      */
37078     focusClass : "x-form-focus",
37079     /**
37080      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37081       automatic validation (defaults to "keyup").
37082      */
37083     validationEvent : "keyup",
37084     /**
37085      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37086      */
37087     validateOnBlur : true,
37088     /**
37089      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37090      */
37091     validationDelay : 250,
37092     /**
37093      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37094      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37095      */
37096     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37097     /**
37098      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37099      */
37100     fieldClass : "x-form-field",
37101     /**
37102      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37103      *<pre>
37104 Value         Description
37105 -----------   ----------------------------------------------------------------------
37106 qtip          Display a quick tip when the user hovers over the field
37107 title         Display a default browser title attribute popup
37108 under         Add a block div beneath the field containing the error text
37109 side          Add an error icon to the right of the field with a popup on hover
37110 [element id]  Add the error text directly to the innerHTML of the specified element
37111 </pre>
37112      */
37113     msgTarget : 'qtip',
37114     /**
37115      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37116      */
37117     msgFx : 'normal',
37118
37119     /**
37120      * @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.
37121      */
37122     readOnly : false,
37123
37124     /**
37125      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37126      */
37127     disabled : false,
37128
37129     /**
37130      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37131      */
37132     inputType : undefined,
37133     
37134     /**
37135      * @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).
37136          */
37137         tabIndex : undefined,
37138         
37139     // private
37140     isFormField : true,
37141
37142     // private
37143     hasFocus : false,
37144     /**
37145      * @property {Roo.Element} fieldEl
37146      * Element Containing the rendered Field (with label etc.)
37147      */
37148     /**
37149      * @cfg {Mixed} value A value to initialize this field with.
37150      */
37151     value : undefined,
37152
37153     /**
37154      * @cfg {String} name The field's HTML name attribute.
37155      */
37156     /**
37157      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37158      */
37159
37160         // private ??
37161         initComponent : function(){
37162         Roo.form.Field.superclass.initComponent.call(this);
37163         this.addEvents({
37164             /**
37165              * @event focus
37166              * Fires when this field receives input focus.
37167              * @param {Roo.form.Field} this
37168              */
37169             focus : true,
37170             /**
37171              * @event blur
37172              * Fires when this field loses input focus.
37173              * @param {Roo.form.Field} this
37174              */
37175             blur : true,
37176             /**
37177              * @event specialkey
37178              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37179              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37180              * @param {Roo.form.Field} this
37181              * @param {Roo.EventObject} e The event object
37182              */
37183             specialkey : true,
37184             /**
37185              * @event change
37186              * Fires just before the field blurs if the field value has changed.
37187              * @param {Roo.form.Field} this
37188              * @param {Mixed} newValue The new value
37189              * @param {Mixed} oldValue The original value
37190              */
37191             change : true,
37192             /**
37193              * @event invalid
37194              * Fires after the field has been marked as invalid.
37195              * @param {Roo.form.Field} this
37196              * @param {String} msg The validation message
37197              */
37198             invalid : true,
37199             /**
37200              * @event valid
37201              * Fires after the field has been validated with no errors.
37202              * @param {Roo.form.Field} this
37203              */
37204             valid : true,
37205              /**
37206              * @event keyup
37207              * Fires after the key up
37208              * @param {Roo.form.Field} this
37209              * @param {Roo.EventObject}  e The event Object
37210              */
37211             keyup : true
37212         });
37213     },
37214
37215     /**
37216      * Returns the name attribute of the field if available
37217      * @return {String} name The field name
37218      */
37219     getName: function(){
37220          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37221     },
37222
37223     // private
37224     onRender : function(ct, position){
37225         Roo.form.Field.superclass.onRender.call(this, ct, position);
37226         if(!this.el){
37227             var cfg = this.getAutoCreate();
37228             if(!cfg.name){
37229                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37230             }
37231             if (!cfg.name.length) {
37232                 delete cfg.name;
37233             }
37234             if(this.inputType){
37235                 cfg.type = this.inputType;
37236             }
37237             this.el = ct.createChild(cfg, position);
37238         }
37239         var type = this.el.dom.type;
37240         if(type){
37241             if(type == 'password'){
37242                 type = 'text';
37243             }
37244             this.el.addClass('x-form-'+type);
37245         }
37246         if(this.readOnly){
37247             this.el.dom.readOnly = true;
37248         }
37249         if(this.tabIndex !== undefined){
37250             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37251         }
37252
37253         this.el.addClass([this.fieldClass, this.cls]);
37254         this.initValue();
37255     },
37256
37257     /**
37258      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37259      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37260      * @return {Roo.form.Field} this
37261      */
37262     applyTo : function(target){
37263         this.allowDomMove = false;
37264         this.el = Roo.get(target);
37265         this.render(this.el.dom.parentNode);
37266         return this;
37267     },
37268
37269     // private
37270     initValue : function(){
37271         if(this.value !== undefined){
37272             this.setValue(this.value);
37273         }else if(this.el.dom.value.length > 0){
37274             this.setValue(this.el.dom.value);
37275         }
37276     },
37277
37278     /**
37279      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37280      */
37281     isDirty : function() {
37282         if(this.disabled) {
37283             return false;
37284         }
37285         return String(this.getValue()) !== String(this.originalValue);
37286     },
37287
37288     // private
37289     afterRender : function(){
37290         Roo.form.Field.superclass.afterRender.call(this);
37291         this.initEvents();
37292     },
37293
37294     // private
37295     fireKey : function(e){
37296         //Roo.log('field ' + e.getKey());
37297         if(e.isNavKeyPress()){
37298             this.fireEvent("specialkey", this, e);
37299         }
37300     },
37301
37302     /**
37303      * Resets the current field value to the originally loaded value and clears any validation messages
37304      */
37305     reset : function(){
37306         this.setValue(this.resetValue);
37307         this.clearInvalid();
37308     },
37309
37310     // private
37311     initEvents : function(){
37312         // safari killled keypress - so keydown is now used..
37313         this.el.on("keydown" , this.fireKey,  this);
37314         this.el.on("focus", this.onFocus,  this);
37315         this.el.on("blur", this.onBlur,  this);
37316         this.el.relayEvent('keyup', this);
37317
37318         // reference to original value for reset
37319         this.originalValue = this.getValue();
37320         this.resetValue =  this.getValue();
37321     },
37322
37323     // private
37324     onFocus : function(){
37325         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37326             this.el.addClass(this.focusClass);
37327         }
37328         if(!this.hasFocus){
37329             this.hasFocus = true;
37330             this.startValue = this.getValue();
37331             this.fireEvent("focus", this);
37332         }
37333     },
37334
37335     beforeBlur : Roo.emptyFn,
37336
37337     // private
37338     onBlur : function(){
37339         this.beforeBlur();
37340         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37341             this.el.removeClass(this.focusClass);
37342         }
37343         this.hasFocus = false;
37344         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37345             this.validate();
37346         }
37347         var v = this.getValue();
37348         if(String(v) !== String(this.startValue)){
37349             this.fireEvent('change', this, v, this.startValue);
37350         }
37351         this.fireEvent("blur", this);
37352     },
37353
37354     /**
37355      * Returns whether or not the field value is currently valid
37356      * @param {Boolean} preventMark True to disable marking the field invalid
37357      * @return {Boolean} True if the value is valid, else false
37358      */
37359     isValid : function(preventMark){
37360         if(this.disabled){
37361             return true;
37362         }
37363         var restore = this.preventMark;
37364         this.preventMark = preventMark === true;
37365         var v = this.validateValue(this.processValue(this.getRawValue()));
37366         this.preventMark = restore;
37367         return v;
37368     },
37369
37370     /**
37371      * Validates the field value
37372      * @return {Boolean} True if the value is valid, else false
37373      */
37374     validate : function(){
37375         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37376             this.clearInvalid();
37377             return true;
37378         }
37379         return false;
37380     },
37381
37382     processValue : function(value){
37383         return value;
37384     },
37385
37386     // private
37387     // Subclasses should provide the validation implementation by overriding this
37388     validateValue : function(value){
37389         return true;
37390     },
37391
37392     /**
37393      * Mark this field as invalid
37394      * @param {String} msg The validation message
37395      */
37396     markInvalid : function(msg){
37397         if(!this.rendered || this.preventMark){ // not rendered
37398             return;
37399         }
37400         
37401         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37402         
37403         obj.el.addClass(this.invalidClass);
37404         msg = msg || this.invalidText;
37405         switch(this.msgTarget){
37406             case 'qtip':
37407                 obj.el.dom.qtip = msg;
37408                 obj.el.dom.qclass = 'x-form-invalid-tip';
37409                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37410                     Roo.QuickTips.enable();
37411                 }
37412                 break;
37413             case 'title':
37414                 this.el.dom.title = msg;
37415                 break;
37416             case 'under':
37417                 if(!this.errorEl){
37418                     var elp = this.el.findParent('.x-form-element', 5, true);
37419                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37420                     this.errorEl.setWidth(elp.getWidth(true)-20);
37421                 }
37422                 this.errorEl.update(msg);
37423                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37424                 break;
37425             case 'side':
37426                 if(!this.errorIcon){
37427                     var elp = this.el.findParent('.x-form-element', 5, true);
37428                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37429                 }
37430                 this.alignErrorIcon();
37431                 this.errorIcon.dom.qtip = msg;
37432                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37433                 this.errorIcon.show();
37434                 this.on('resize', this.alignErrorIcon, this);
37435                 break;
37436             default:
37437                 var t = Roo.getDom(this.msgTarget);
37438                 t.innerHTML = msg;
37439                 t.style.display = this.msgDisplay;
37440                 break;
37441         }
37442         this.fireEvent('invalid', this, msg);
37443     },
37444
37445     // private
37446     alignErrorIcon : function(){
37447         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37448     },
37449
37450     /**
37451      * Clear any invalid styles/messages for this field
37452      */
37453     clearInvalid : function(){
37454         if(!this.rendered || this.preventMark){ // not rendered
37455             return;
37456         }
37457         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37458         
37459         obj.el.removeClass(this.invalidClass);
37460         switch(this.msgTarget){
37461             case 'qtip':
37462                 obj.el.dom.qtip = '';
37463                 break;
37464             case 'title':
37465                 this.el.dom.title = '';
37466                 break;
37467             case 'under':
37468                 if(this.errorEl){
37469                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37470                 }
37471                 break;
37472             case 'side':
37473                 if(this.errorIcon){
37474                     this.errorIcon.dom.qtip = '';
37475                     this.errorIcon.hide();
37476                     this.un('resize', this.alignErrorIcon, this);
37477                 }
37478                 break;
37479             default:
37480                 var t = Roo.getDom(this.msgTarget);
37481                 t.innerHTML = '';
37482                 t.style.display = 'none';
37483                 break;
37484         }
37485         this.fireEvent('valid', this);
37486     },
37487
37488     /**
37489      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37490      * @return {Mixed} value The field value
37491      */
37492     getRawValue : function(){
37493         var v = this.el.getValue();
37494         
37495         return v;
37496     },
37497
37498     /**
37499      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37500      * @return {Mixed} value The field value
37501      */
37502     getValue : function(){
37503         var v = this.el.getValue();
37504          
37505         return v;
37506     },
37507
37508     /**
37509      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37510      * @param {Mixed} value The value to set
37511      */
37512     setRawValue : function(v){
37513         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37514     },
37515
37516     /**
37517      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37518      * @param {Mixed} value The value to set
37519      */
37520     setValue : function(v){
37521         this.value = v;
37522         if(this.rendered){
37523             this.el.dom.value = (v === null || v === undefined ? '' : v);
37524              this.validate();
37525         }
37526     },
37527
37528     adjustSize : function(w, h){
37529         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37530         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37531         return s;
37532     },
37533
37534     adjustWidth : function(tag, w){
37535         tag = tag.toLowerCase();
37536         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37537             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37538                 if(tag == 'input'){
37539                     return w + 2;
37540                 }
37541                 if(tag == 'textarea'){
37542                     return w-2;
37543                 }
37544             }else if(Roo.isOpera){
37545                 if(tag == 'input'){
37546                     return w + 2;
37547                 }
37548                 if(tag == 'textarea'){
37549                     return w-2;
37550                 }
37551             }
37552         }
37553         return w;
37554     }
37555 });
37556
37557
37558 // anything other than normal should be considered experimental
37559 Roo.form.Field.msgFx = {
37560     normal : {
37561         show: function(msgEl, f){
37562             msgEl.setDisplayed('block');
37563         },
37564
37565         hide : function(msgEl, f){
37566             msgEl.setDisplayed(false).update('');
37567         }
37568     },
37569
37570     slide : {
37571         show: function(msgEl, f){
37572             msgEl.slideIn('t', {stopFx:true});
37573         },
37574
37575         hide : function(msgEl, f){
37576             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37577         }
37578     },
37579
37580     slideRight : {
37581         show: function(msgEl, f){
37582             msgEl.fixDisplay();
37583             msgEl.alignTo(f.el, 'tl-tr');
37584             msgEl.slideIn('l', {stopFx:true});
37585         },
37586
37587         hide : function(msgEl, f){
37588             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37589         }
37590     }
37591 };/*
37592  * Based on:
37593  * Ext JS Library 1.1.1
37594  * Copyright(c) 2006-2007, Ext JS, LLC.
37595  *
37596  * Originally Released Under LGPL - original licence link has changed is not relivant.
37597  *
37598  * Fork - LGPL
37599  * <script type="text/javascript">
37600  */
37601  
37602
37603 /**
37604  * @class Roo.form.TextField
37605  * @extends Roo.form.Field
37606  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37607  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37608  * @constructor
37609  * Creates a new TextField
37610  * @param {Object} config Configuration options
37611  */
37612 Roo.form.TextField = function(config){
37613     Roo.form.TextField.superclass.constructor.call(this, config);
37614     this.addEvents({
37615         /**
37616          * @event autosize
37617          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37618          * according to the default logic, but this event provides a hook for the developer to apply additional
37619          * logic at runtime to resize the field if needed.
37620              * @param {Roo.form.Field} this This text field
37621              * @param {Number} width The new field width
37622              */
37623         autosize : true
37624     });
37625 };
37626
37627 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37628     /**
37629      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37630      */
37631     grow : false,
37632     /**
37633      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37634      */
37635     growMin : 30,
37636     /**
37637      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37638      */
37639     growMax : 800,
37640     /**
37641      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37642      */
37643     vtype : null,
37644     /**
37645      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37646      */
37647     maskRe : null,
37648     /**
37649      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37650      */
37651     disableKeyFilter : false,
37652     /**
37653      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37654      */
37655     allowBlank : true,
37656     /**
37657      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37658      */
37659     minLength : 0,
37660     /**
37661      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37662      */
37663     maxLength : Number.MAX_VALUE,
37664     /**
37665      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37666      */
37667     minLengthText : "The minimum length for this field is {0}",
37668     /**
37669      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37670      */
37671     maxLengthText : "The maximum length for this field is {0}",
37672     /**
37673      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37674      */
37675     selectOnFocus : false,
37676     /**
37677      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37678      */
37679     blankText : "This field is required",
37680     /**
37681      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37682      * If available, this function will be called only after the basic validators all return true, and will be passed the
37683      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37684      */
37685     validator : null,
37686     /**
37687      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37688      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37689      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37690      */
37691     regex : null,
37692     /**
37693      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37694      */
37695     regexText : "",
37696     /**
37697      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37698      */
37699     emptyText : null,
37700    
37701
37702     // private
37703     initEvents : function()
37704     {
37705         if (this.emptyText) {
37706             this.el.attr('placeholder', this.emptyText);
37707         }
37708         
37709         Roo.form.TextField.superclass.initEvents.call(this);
37710         if(this.validationEvent == 'keyup'){
37711             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37712             this.el.on('keyup', this.filterValidation, this);
37713         }
37714         else if(this.validationEvent !== false){
37715             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37716         }
37717         
37718         if(this.selectOnFocus){
37719             this.on("focus", this.preFocus, this);
37720             
37721         }
37722         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37723             this.el.on("keypress", this.filterKeys, this);
37724         }
37725         if(this.grow){
37726             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37727             this.el.on("click", this.autoSize,  this);
37728         }
37729         if(this.el.is('input[type=password]') && Roo.isSafari){
37730             this.el.on('keydown', this.SafariOnKeyDown, this);
37731         }
37732     },
37733
37734     processValue : function(value){
37735         if(this.stripCharsRe){
37736             var newValue = value.replace(this.stripCharsRe, '');
37737             if(newValue !== value){
37738                 this.setRawValue(newValue);
37739                 return newValue;
37740             }
37741         }
37742         return value;
37743     },
37744
37745     filterValidation : function(e){
37746         if(!e.isNavKeyPress()){
37747             this.validationTask.delay(this.validationDelay);
37748         }
37749     },
37750
37751     // private
37752     onKeyUp : function(e){
37753         if(!e.isNavKeyPress()){
37754             this.autoSize();
37755         }
37756     },
37757
37758     /**
37759      * Resets the current field value to the originally-loaded value and clears any validation messages.
37760      *  
37761      */
37762     reset : function(){
37763         Roo.form.TextField.superclass.reset.call(this);
37764        
37765     },
37766
37767     
37768     // private
37769     preFocus : function(){
37770         
37771         if(this.selectOnFocus){
37772             this.el.dom.select();
37773         }
37774     },
37775
37776     
37777     // private
37778     filterKeys : function(e){
37779         var k = e.getKey();
37780         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37781             return;
37782         }
37783         var c = e.getCharCode(), cc = String.fromCharCode(c);
37784         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37785             return;
37786         }
37787         if(!this.maskRe.test(cc)){
37788             e.stopEvent();
37789         }
37790     },
37791
37792     setValue : function(v){
37793         
37794         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37795         
37796         this.autoSize();
37797     },
37798
37799     /**
37800      * Validates a value according to the field's validation rules and marks the field as invalid
37801      * if the validation fails
37802      * @param {Mixed} value The value to validate
37803      * @return {Boolean} True if the value is valid, else false
37804      */
37805     validateValue : function(value){
37806         if(value.length < 1)  { // if it's blank
37807              if(this.allowBlank){
37808                 this.clearInvalid();
37809                 return true;
37810              }else{
37811                 this.markInvalid(this.blankText);
37812                 return false;
37813              }
37814         }
37815         if(value.length < this.minLength){
37816             this.markInvalid(String.format(this.minLengthText, this.minLength));
37817             return false;
37818         }
37819         if(value.length > this.maxLength){
37820             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37821             return false;
37822         }
37823         if(this.vtype){
37824             var vt = Roo.form.VTypes;
37825             if(!vt[this.vtype](value, this)){
37826                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37827                 return false;
37828             }
37829         }
37830         if(typeof this.validator == "function"){
37831             var msg = this.validator(value);
37832             if(msg !== true){
37833                 this.markInvalid(msg);
37834                 return false;
37835             }
37836         }
37837         if(this.regex && !this.regex.test(value)){
37838             this.markInvalid(this.regexText);
37839             return false;
37840         }
37841         return true;
37842     },
37843
37844     /**
37845      * Selects text in this field
37846      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37847      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37848      */
37849     selectText : function(start, end){
37850         var v = this.getRawValue();
37851         if(v.length > 0){
37852             start = start === undefined ? 0 : start;
37853             end = end === undefined ? v.length : end;
37854             var d = this.el.dom;
37855             if(d.setSelectionRange){
37856                 d.setSelectionRange(start, end);
37857             }else if(d.createTextRange){
37858                 var range = d.createTextRange();
37859                 range.moveStart("character", start);
37860                 range.moveEnd("character", v.length-end);
37861                 range.select();
37862             }
37863         }
37864     },
37865
37866     /**
37867      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37868      * This only takes effect if grow = true, and fires the autosize event.
37869      */
37870     autoSize : function(){
37871         if(!this.grow || !this.rendered){
37872             return;
37873         }
37874         if(!this.metrics){
37875             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37876         }
37877         var el = this.el;
37878         var v = el.dom.value;
37879         var d = document.createElement('div');
37880         d.appendChild(document.createTextNode(v));
37881         v = d.innerHTML;
37882         d = null;
37883         v += "&#160;";
37884         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37885         this.el.setWidth(w);
37886         this.fireEvent("autosize", this, w);
37887     },
37888     
37889     // private
37890     SafariOnKeyDown : function(event)
37891     {
37892         // this is a workaround for a password hang bug on chrome/ webkit.
37893         
37894         var isSelectAll = false;
37895         
37896         if(this.el.dom.selectionEnd > 0){
37897             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37898         }
37899         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37900             event.preventDefault();
37901             this.setValue('');
37902             return;
37903         }
37904         
37905         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37906             
37907             event.preventDefault();
37908             // this is very hacky as keydown always get's upper case.
37909             
37910             var cc = String.fromCharCode(event.getCharCode());
37911             
37912             
37913             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37914             
37915         }
37916         
37917         
37918     }
37919 });/*
37920  * Based on:
37921  * Ext JS Library 1.1.1
37922  * Copyright(c) 2006-2007, Ext JS, LLC.
37923  *
37924  * Originally Released Under LGPL - original licence link has changed is not relivant.
37925  *
37926  * Fork - LGPL
37927  * <script type="text/javascript">
37928  */
37929  
37930 /**
37931  * @class Roo.form.Hidden
37932  * @extends Roo.form.TextField
37933  * Simple Hidden element used on forms 
37934  * 
37935  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37936  * 
37937  * @constructor
37938  * Creates a new Hidden form element.
37939  * @param {Object} config Configuration options
37940  */
37941
37942
37943
37944 // easy hidden field...
37945 Roo.form.Hidden = function(config){
37946     Roo.form.Hidden.superclass.constructor.call(this, config);
37947 };
37948   
37949 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37950     fieldLabel:      '',
37951     inputType:      'hidden',
37952     width:          50,
37953     allowBlank:     true,
37954     labelSeparator: '',
37955     hidden:         true,
37956     itemCls :       'x-form-item-display-none'
37957
37958
37959 });
37960
37961
37962 /*
37963  * Based on:
37964  * Ext JS Library 1.1.1
37965  * Copyright(c) 2006-2007, Ext JS, LLC.
37966  *
37967  * Originally Released Under LGPL - original licence link has changed is not relivant.
37968  *
37969  * Fork - LGPL
37970  * <script type="text/javascript">
37971  */
37972  
37973 /**
37974  * @class Roo.form.TriggerField
37975  * @extends Roo.form.TextField
37976  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37977  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37978  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37979  * for which you can provide a custom implementation.  For example:
37980  * <pre><code>
37981 var trigger = new Roo.form.TriggerField();
37982 trigger.onTriggerClick = myTriggerFn;
37983 trigger.applyTo('my-field');
37984 </code></pre>
37985  *
37986  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37987  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37988  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37989  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37990  * @constructor
37991  * Create a new TriggerField.
37992  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37993  * to the base TextField)
37994  */
37995 Roo.form.TriggerField = function(config){
37996     this.mimicing = false;
37997     Roo.form.TriggerField.superclass.constructor.call(this, config);
37998 };
37999
38000 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
38001     /**
38002      * @cfg {String} triggerClass A CSS class to apply to the trigger
38003      */
38004     /**
38005      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38006      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38007      */
38008     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38009     /**
38010      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38011      */
38012     hideTrigger:false,
38013
38014     /** @cfg {Boolean} grow @hide */
38015     /** @cfg {Number} growMin @hide */
38016     /** @cfg {Number} growMax @hide */
38017
38018     /**
38019      * @hide 
38020      * @method
38021      */
38022     autoSize: Roo.emptyFn,
38023     // private
38024     monitorTab : true,
38025     // private
38026     deferHeight : true,
38027
38028     
38029     actionMode : 'wrap',
38030     // private
38031     onResize : function(w, h){
38032         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38033         if(typeof w == 'number'){
38034             var x = w - this.trigger.getWidth();
38035             this.el.setWidth(this.adjustWidth('input', x));
38036             this.trigger.setStyle('left', x+'px');
38037         }
38038     },
38039
38040     // private
38041     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38042
38043     // private
38044     getResizeEl : function(){
38045         return this.wrap;
38046     },
38047
38048     // private
38049     getPositionEl : function(){
38050         return this.wrap;
38051     },
38052
38053     // private
38054     alignErrorIcon : function(){
38055         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38056     },
38057
38058     // private
38059     onRender : function(ct, position){
38060         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38061         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38062         this.trigger = this.wrap.createChild(this.triggerConfig ||
38063                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38064         if(this.hideTrigger){
38065             this.trigger.setDisplayed(false);
38066         }
38067         this.initTrigger();
38068         if(!this.width){
38069             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38070         }
38071     },
38072
38073     // private
38074     initTrigger : function(){
38075         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38076         this.trigger.addClassOnOver('x-form-trigger-over');
38077         this.trigger.addClassOnClick('x-form-trigger-click');
38078     },
38079
38080     // private
38081     onDestroy : function(){
38082         if(this.trigger){
38083             this.trigger.removeAllListeners();
38084             this.trigger.remove();
38085         }
38086         if(this.wrap){
38087             this.wrap.remove();
38088         }
38089         Roo.form.TriggerField.superclass.onDestroy.call(this);
38090     },
38091
38092     // private
38093     onFocus : function(){
38094         Roo.form.TriggerField.superclass.onFocus.call(this);
38095         if(!this.mimicing){
38096             this.wrap.addClass('x-trigger-wrap-focus');
38097             this.mimicing = true;
38098             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38099             if(this.monitorTab){
38100                 this.el.on("keydown", this.checkTab, this);
38101             }
38102         }
38103     },
38104
38105     // private
38106     checkTab : function(e){
38107         if(e.getKey() == e.TAB){
38108             this.triggerBlur();
38109         }
38110     },
38111
38112     // private
38113     onBlur : function(){
38114         // do nothing
38115     },
38116
38117     // private
38118     mimicBlur : function(e, t){
38119         if(!this.wrap.contains(t) && this.validateBlur()){
38120             this.triggerBlur();
38121         }
38122     },
38123
38124     // private
38125     triggerBlur : function(){
38126         this.mimicing = false;
38127         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38128         if(this.monitorTab){
38129             this.el.un("keydown", this.checkTab, this);
38130         }
38131         this.wrap.removeClass('x-trigger-wrap-focus');
38132         Roo.form.TriggerField.superclass.onBlur.call(this);
38133     },
38134
38135     // private
38136     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38137     validateBlur : function(e, t){
38138         return true;
38139     },
38140
38141     // private
38142     onDisable : function(){
38143         Roo.form.TriggerField.superclass.onDisable.call(this);
38144         if(this.wrap){
38145             this.wrap.addClass('x-item-disabled');
38146         }
38147     },
38148
38149     // private
38150     onEnable : function(){
38151         Roo.form.TriggerField.superclass.onEnable.call(this);
38152         if(this.wrap){
38153             this.wrap.removeClass('x-item-disabled');
38154         }
38155     },
38156
38157     // private
38158     onShow : function(){
38159         var ae = this.getActionEl();
38160         
38161         if(ae){
38162             ae.dom.style.display = '';
38163             ae.dom.style.visibility = 'visible';
38164         }
38165     },
38166
38167     // private
38168     
38169     onHide : function(){
38170         var ae = this.getActionEl();
38171         ae.dom.style.display = 'none';
38172     },
38173
38174     /**
38175      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38176      * by an implementing function.
38177      * @method
38178      * @param {EventObject} e
38179      */
38180     onTriggerClick : Roo.emptyFn
38181 });
38182
38183 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38184 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38185 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38186 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38187     initComponent : function(){
38188         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38189
38190         this.triggerConfig = {
38191             tag:'span', cls:'x-form-twin-triggers', cn:[
38192             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38193             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38194         ]};
38195     },
38196
38197     getTrigger : function(index){
38198         return this.triggers[index];
38199     },
38200
38201     initTrigger : function(){
38202         var ts = this.trigger.select('.x-form-trigger', true);
38203         this.wrap.setStyle('overflow', 'hidden');
38204         var triggerField = this;
38205         ts.each(function(t, all, index){
38206             t.hide = function(){
38207                 var w = triggerField.wrap.getWidth();
38208                 this.dom.style.display = 'none';
38209                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38210             };
38211             t.show = function(){
38212                 var w = triggerField.wrap.getWidth();
38213                 this.dom.style.display = '';
38214                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38215             };
38216             var triggerIndex = 'Trigger'+(index+1);
38217
38218             if(this['hide'+triggerIndex]){
38219                 t.dom.style.display = 'none';
38220             }
38221             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38222             t.addClassOnOver('x-form-trigger-over');
38223             t.addClassOnClick('x-form-trigger-click');
38224         }, this);
38225         this.triggers = ts.elements;
38226     },
38227
38228     onTrigger1Click : Roo.emptyFn,
38229     onTrigger2Click : Roo.emptyFn
38230 });/*
38231  * Based on:
38232  * Ext JS Library 1.1.1
38233  * Copyright(c) 2006-2007, Ext JS, LLC.
38234  *
38235  * Originally Released Under LGPL - original licence link has changed is not relivant.
38236  *
38237  * Fork - LGPL
38238  * <script type="text/javascript">
38239  */
38240  
38241 /**
38242  * @class Roo.form.TextArea
38243  * @extends Roo.form.TextField
38244  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38245  * support for auto-sizing.
38246  * @constructor
38247  * Creates a new TextArea
38248  * @param {Object} config Configuration options
38249  */
38250 Roo.form.TextArea = function(config){
38251     Roo.form.TextArea.superclass.constructor.call(this, config);
38252     // these are provided exchanges for backwards compat
38253     // minHeight/maxHeight were replaced by growMin/growMax to be
38254     // compatible with TextField growing config values
38255     if(this.minHeight !== undefined){
38256         this.growMin = this.minHeight;
38257     }
38258     if(this.maxHeight !== undefined){
38259         this.growMax = this.maxHeight;
38260     }
38261 };
38262
38263 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38264     /**
38265      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38266      */
38267     growMin : 60,
38268     /**
38269      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38270      */
38271     growMax: 1000,
38272     /**
38273      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38274      * in the field (equivalent to setting overflow: hidden, defaults to false)
38275      */
38276     preventScrollbars: false,
38277     /**
38278      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38279      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38280      */
38281
38282     // private
38283     onRender : function(ct, position){
38284         if(!this.el){
38285             this.defaultAutoCreate = {
38286                 tag: "textarea",
38287                 style:"width:300px;height:60px;",
38288                 autocomplete: "new-password"
38289             };
38290         }
38291         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38292         if(this.grow){
38293             this.textSizeEl = Roo.DomHelper.append(document.body, {
38294                 tag: "pre", cls: "x-form-grow-sizer"
38295             });
38296             if(this.preventScrollbars){
38297                 this.el.setStyle("overflow", "hidden");
38298             }
38299             this.el.setHeight(this.growMin);
38300         }
38301     },
38302
38303     onDestroy : function(){
38304         if(this.textSizeEl){
38305             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38306         }
38307         Roo.form.TextArea.superclass.onDestroy.call(this);
38308     },
38309
38310     // private
38311     onKeyUp : function(e){
38312         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38313             this.autoSize();
38314         }
38315     },
38316
38317     /**
38318      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38319      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38320      */
38321     autoSize : function(){
38322         if(!this.grow || !this.textSizeEl){
38323             return;
38324         }
38325         var el = this.el;
38326         var v = el.dom.value;
38327         var ts = this.textSizeEl;
38328
38329         ts.innerHTML = '';
38330         ts.appendChild(document.createTextNode(v));
38331         v = ts.innerHTML;
38332
38333         Roo.fly(ts).setWidth(this.el.getWidth());
38334         if(v.length < 1){
38335             v = "&#160;&#160;";
38336         }else{
38337             if(Roo.isIE){
38338                 v = v.replace(/\n/g, '<p>&#160;</p>');
38339             }
38340             v += "&#160;\n&#160;";
38341         }
38342         ts.innerHTML = v;
38343         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38344         if(h != this.lastHeight){
38345             this.lastHeight = h;
38346             this.el.setHeight(h);
38347             this.fireEvent("autosize", this, h);
38348         }
38349     }
38350 });/*
38351  * Based on:
38352  * Ext JS Library 1.1.1
38353  * Copyright(c) 2006-2007, Ext JS, LLC.
38354  *
38355  * Originally Released Under LGPL - original licence link has changed is not relivant.
38356  *
38357  * Fork - LGPL
38358  * <script type="text/javascript">
38359  */
38360  
38361
38362 /**
38363  * @class Roo.form.NumberField
38364  * @extends Roo.form.TextField
38365  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38366  * @constructor
38367  * Creates a new NumberField
38368  * @param {Object} config Configuration options
38369  */
38370 Roo.form.NumberField = function(config){
38371     Roo.form.NumberField.superclass.constructor.call(this, config);
38372 };
38373
38374 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38375     /**
38376      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38377      */
38378     fieldClass: "x-form-field x-form-num-field",
38379     /**
38380      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38381      */
38382     allowDecimals : true,
38383     /**
38384      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38385      */
38386     decimalSeparator : ".",
38387     /**
38388      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38389      */
38390     decimalPrecision : 2,
38391     /**
38392      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38393      */
38394     allowNegative : true,
38395     /**
38396      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38397      */
38398     minValue : Number.NEGATIVE_INFINITY,
38399     /**
38400      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38401      */
38402     maxValue : Number.MAX_VALUE,
38403     /**
38404      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38405      */
38406     minText : "The minimum value for this field is {0}",
38407     /**
38408      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38409      */
38410     maxText : "The maximum value for this field is {0}",
38411     /**
38412      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38413      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38414      */
38415     nanText : "{0} is not a valid number",
38416
38417     // private
38418     initEvents : function(){
38419         Roo.form.NumberField.superclass.initEvents.call(this);
38420         var allowed = "0123456789";
38421         if(this.allowDecimals){
38422             allowed += this.decimalSeparator;
38423         }
38424         if(this.allowNegative){
38425             allowed += "-";
38426         }
38427         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38428         var keyPress = function(e){
38429             var k = e.getKey();
38430             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38431                 return;
38432             }
38433             var c = e.getCharCode();
38434             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38435                 e.stopEvent();
38436             }
38437         };
38438         this.el.on("keypress", keyPress, this);
38439     },
38440
38441     // private
38442     validateValue : function(value){
38443         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38444             return false;
38445         }
38446         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38447              return true;
38448         }
38449         var num = this.parseValue(value);
38450         if(isNaN(num)){
38451             this.markInvalid(String.format(this.nanText, value));
38452             return false;
38453         }
38454         if(num < this.minValue){
38455             this.markInvalid(String.format(this.minText, this.minValue));
38456             return false;
38457         }
38458         if(num > this.maxValue){
38459             this.markInvalid(String.format(this.maxText, this.maxValue));
38460             return false;
38461         }
38462         return true;
38463     },
38464
38465     getValue : function(){
38466         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38467     },
38468
38469     // private
38470     parseValue : function(value){
38471         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38472         return isNaN(value) ? '' : value;
38473     },
38474
38475     // private
38476     fixPrecision : function(value){
38477         var nan = isNaN(value);
38478         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38479             return nan ? '' : value;
38480         }
38481         return parseFloat(value).toFixed(this.decimalPrecision);
38482     },
38483
38484     setValue : function(v){
38485         v = this.fixPrecision(v);
38486         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38487     },
38488
38489     // private
38490     decimalPrecisionFcn : function(v){
38491         return Math.floor(v);
38492     },
38493
38494     beforeBlur : function(){
38495         var v = this.parseValue(this.getRawValue());
38496         if(v){
38497             this.setValue(v);
38498         }
38499     }
38500 });/*
38501  * Based on:
38502  * Ext JS Library 1.1.1
38503  * Copyright(c) 2006-2007, Ext JS, LLC.
38504  *
38505  * Originally Released Under LGPL - original licence link has changed is not relivant.
38506  *
38507  * Fork - LGPL
38508  * <script type="text/javascript">
38509  */
38510  
38511 /**
38512  * @class Roo.form.DateField
38513  * @extends Roo.form.TriggerField
38514  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38515 * @constructor
38516 * Create a new DateField
38517 * @param {Object} config
38518  */
38519 Roo.form.DateField = function(config){
38520     Roo.form.DateField.superclass.constructor.call(this, config);
38521     
38522       this.addEvents({
38523          
38524         /**
38525          * @event select
38526          * Fires when a date is selected
38527              * @param {Roo.form.DateField} combo This combo box
38528              * @param {Date} date The date selected
38529              */
38530         'select' : true
38531          
38532     });
38533     
38534     
38535     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38536     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38537     this.ddMatch = null;
38538     if(this.disabledDates){
38539         var dd = this.disabledDates;
38540         var re = "(?:";
38541         for(var i = 0; i < dd.length; i++){
38542             re += dd[i];
38543             if(i != dd.length-1) re += "|";
38544         }
38545         this.ddMatch = new RegExp(re + ")");
38546     }
38547 };
38548
38549 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38550     /**
38551      * @cfg {String} format
38552      * The default date format string which can be overriden for localization support.  The format must be
38553      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38554      */
38555     format : "m/d/y",
38556     /**
38557      * @cfg {String} altFormats
38558      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38559      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38560      */
38561     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38562     /**
38563      * @cfg {Array} disabledDays
38564      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38565      */
38566     disabledDays : null,
38567     /**
38568      * @cfg {String} disabledDaysText
38569      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38570      */
38571     disabledDaysText : "Disabled",
38572     /**
38573      * @cfg {Array} disabledDates
38574      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38575      * expression so they are very powerful. Some examples:
38576      * <ul>
38577      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38578      * <li>["03/08", "09/16"] would disable those days for every year</li>
38579      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38580      * <li>["03/../2006"] would disable every day in March 2006</li>
38581      * <li>["^03"] would disable every day in every March</li>
38582      * </ul>
38583      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38584      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38585      */
38586     disabledDates : null,
38587     /**
38588      * @cfg {String} disabledDatesText
38589      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38590      */
38591     disabledDatesText : "Disabled",
38592     /**
38593      * @cfg {Date/String} minValue
38594      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38595      * valid format (defaults to null).
38596      */
38597     minValue : null,
38598     /**
38599      * @cfg {Date/String} maxValue
38600      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38601      * valid format (defaults to null).
38602      */
38603     maxValue : null,
38604     /**
38605      * @cfg {String} minText
38606      * The error text to display when the date in the cell is before minValue (defaults to
38607      * 'The date in this field must be after {minValue}').
38608      */
38609     minText : "The date in this field must be equal to or after {0}",
38610     /**
38611      * @cfg {String} maxText
38612      * The error text to display when the date in the cell is after maxValue (defaults to
38613      * 'The date in this field must be before {maxValue}').
38614      */
38615     maxText : "The date in this field must be equal to or before {0}",
38616     /**
38617      * @cfg {String} invalidText
38618      * The error text to display when the date in the field is invalid (defaults to
38619      * '{value} is not a valid date - it must be in the format {format}').
38620      */
38621     invalidText : "{0} is not a valid date - it must be in the format {1}",
38622     /**
38623      * @cfg {String} triggerClass
38624      * An additional CSS class used to style the trigger button.  The trigger will always get the
38625      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38626      * which displays a calendar icon).
38627      */
38628     triggerClass : 'x-form-date-trigger',
38629     
38630
38631     /**
38632      * @cfg {Boolean} useIso
38633      * if enabled, then the date field will use a hidden field to store the 
38634      * real value as iso formated date. default (false)
38635      */ 
38636     useIso : false,
38637     /**
38638      * @cfg {String/Object} autoCreate
38639      * A DomHelper element spec, or true for a default element spec (defaults to
38640      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38641      */ 
38642     // private
38643     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38644     
38645     // private
38646     hiddenField: false,
38647     
38648     onRender : function(ct, position)
38649     {
38650         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38651         if (this.useIso) {
38652             //this.el.dom.removeAttribute('name'); 
38653             Roo.log("Changing name?");
38654             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38655             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38656                     'before', true);
38657             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38658             // prevent input submission
38659             this.hiddenName = this.name;
38660         }
38661             
38662             
38663     },
38664     
38665     // private
38666     validateValue : function(value)
38667     {
38668         value = this.formatDate(value);
38669         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38670             Roo.log('super failed');
38671             return false;
38672         }
38673         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38674              return true;
38675         }
38676         var svalue = value;
38677         value = this.parseDate(value);
38678         if(!value){
38679             Roo.log('parse date failed' + svalue);
38680             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38681             return false;
38682         }
38683         var time = value.getTime();
38684         if(this.minValue && time < this.minValue.getTime()){
38685             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38686             return false;
38687         }
38688         if(this.maxValue && time > this.maxValue.getTime()){
38689             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38690             return false;
38691         }
38692         if(this.disabledDays){
38693             var day = value.getDay();
38694             for(var i = 0; i < this.disabledDays.length; i++) {
38695                 if(day === this.disabledDays[i]){
38696                     this.markInvalid(this.disabledDaysText);
38697                     return false;
38698                 }
38699             }
38700         }
38701         var fvalue = this.formatDate(value);
38702         if(this.ddMatch && this.ddMatch.test(fvalue)){
38703             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38704             return false;
38705         }
38706         return true;
38707     },
38708
38709     // private
38710     // Provides logic to override the default TriggerField.validateBlur which just returns true
38711     validateBlur : function(){
38712         return !this.menu || !this.menu.isVisible();
38713     },
38714     
38715     getName: function()
38716     {
38717         // returns hidden if it's set..
38718         if (!this.rendered) {return ''};
38719         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38720         
38721     },
38722
38723     /**
38724      * Returns the current date value of the date field.
38725      * @return {Date} The date value
38726      */
38727     getValue : function(){
38728         
38729         return  this.hiddenField ?
38730                 this.hiddenField.value :
38731                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38732     },
38733
38734     /**
38735      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38736      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38737      * (the default format used is "m/d/y").
38738      * <br />Usage:
38739      * <pre><code>
38740 //All of these calls set the same date value (May 4, 2006)
38741
38742 //Pass a date object:
38743 var dt = new Date('5/4/06');
38744 dateField.setValue(dt);
38745
38746 //Pass a date string (default format):
38747 dateField.setValue('5/4/06');
38748
38749 //Pass a date string (custom format):
38750 dateField.format = 'Y-m-d';
38751 dateField.setValue('2006-5-4');
38752 </code></pre>
38753      * @param {String/Date} date The date or valid date string
38754      */
38755     setValue : function(date){
38756         if (this.hiddenField) {
38757             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38758         }
38759         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38760         // make sure the value field is always stored as a date..
38761         this.value = this.parseDate(date);
38762         
38763         
38764     },
38765
38766     // private
38767     parseDate : function(value){
38768         if(!value || value instanceof Date){
38769             return value;
38770         }
38771         var v = Date.parseDate(value, this.format);
38772          if (!v && this.useIso) {
38773             v = Date.parseDate(value, 'Y-m-d');
38774         }
38775         if(!v && this.altFormats){
38776             if(!this.altFormatsArray){
38777                 this.altFormatsArray = this.altFormats.split("|");
38778             }
38779             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38780                 v = Date.parseDate(value, this.altFormatsArray[i]);
38781             }
38782         }
38783         return v;
38784     },
38785
38786     // private
38787     formatDate : function(date, fmt){
38788         return (!date || !(date instanceof Date)) ?
38789                date : date.dateFormat(fmt || this.format);
38790     },
38791
38792     // private
38793     menuListeners : {
38794         select: function(m, d){
38795             
38796             this.setValue(d);
38797             this.fireEvent('select', this, d);
38798         },
38799         show : function(){ // retain focus styling
38800             this.onFocus();
38801         },
38802         hide : function(){
38803             this.focus.defer(10, this);
38804             var ml = this.menuListeners;
38805             this.menu.un("select", ml.select,  this);
38806             this.menu.un("show", ml.show,  this);
38807             this.menu.un("hide", ml.hide,  this);
38808         }
38809     },
38810
38811     // private
38812     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38813     onTriggerClick : function(){
38814         if(this.disabled){
38815             return;
38816         }
38817         if(this.menu == null){
38818             this.menu = new Roo.menu.DateMenu();
38819         }
38820         Roo.apply(this.menu.picker,  {
38821             showClear: this.allowBlank,
38822             minDate : this.minValue,
38823             maxDate : this.maxValue,
38824             disabledDatesRE : this.ddMatch,
38825             disabledDatesText : this.disabledDatesText,
38826             disabledDays : this.disabledDays,
38827             disabledDaysText : this.disabledDaysText,
38828             format : this.useIso ? 'Y-m-d' : this.format,
38829             minText : String.format(this.minText, this.formatDate(this.minValue)),
38830             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38831         });
38832         this.menu.on(Roo.apply({}, this.menuListeners, {
38833             scope:this
38834         }));
38835         this.menu.picker.setValue(this.getValue() || new Date());
38836         this.menu.show(this.el, "tl-bl?");
38837     },
38838
38839     beforeBlur : function(){
38840         var v = this.parseDate(this.getRawValue());
38841         if(v){
38842             this.setValue(v);
38843         }
38844     },
38845
38846     /*@
38847      * overide
38848      * 
38849      */
38850     isDirty : function() {
38851         if(this.disabled) {
38852             return false;
38853         }
38854         
38855         if(typeof(this.startValue) === 'undefined'){
38856             return false;
38857         }
38858         
38859         return String(this.getValue()) !== String(this.startValue);
38860         
38861     }
38862 });/*
38863  * Based on:
38864  * Ext JS Library 1.1.1
38865  * Copyright(c) 2006-2007, Ext JS, LLC.
38866  *
38867  * Originally Released Under LGPL - original licence link has changed is not relivant.
38868  *
38869  * Fork - LGPL
38870  * <script type="text/javascript">
38871  */
38872  
38873 /**
38874  * @class Roo.form.MonthField
38875  * @extends Roo.form.TriggerField
38876  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38877 * @constructor
38878 * Create a new MonthField
38879 * @param {Object} config
38880  */
38881 Roo.form.MonthField = function(config){
38882     
38883     Roo.form.MonthField.superclass.constructor.call(this, config);
38884     
38885       this.addEvents({
38886          
38887         /**
38888          * @event select
38889          * Fires when a date is selected
38890              * @param {Roo.form.MonthFieeld} combo This combo box
38891              * @param {Date} date The date selected
38892              */
38893         'select' : true
38894          
38895     });
38896     
38897     
38898     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38899     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38900     this.ddMatch = null;
38901     if(this.disabledDates){
38902         var dd = this.disabledDates;
38903         var re = "(?:";
38904         for(var i = 0; i < dd.length; i++){
38905             re += dd[i];
38906             if(i != dd.length-1) re += "|";
38907         }
38908         this.ddMatch = new RegExp(re + ")");
38909     }
38910 };
38911
38912 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38913     /**
38914      * @cfg {String} format
38915      * The default date format string which can be overriden for localization support.  The format must be
38916      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38917      */
38918     format : "M Y",
38919     /**
38920      * @cfg {String} altFormats
38921      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38922      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38923      */
38924     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38925     /**
38926      * @cfg {Array} disabledDays
38927      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38928      */
38929     disabledDays : [0,1,2,3,4,5,6],
38930     /**
38931      * @cfg {String} disabledDaysText
38932      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38933      */
38934     disabledDaysText : "Disabled",
38935     /**
38936      * @cfg {Array} disabledDates
38937      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38938      * expression so they are very powerful. Some examples:
38939      * <ul>
38940      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38941      * <li>["03/08", "09/16"] would disable those days for every year</li>
38942      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38943      * <li>["03/../2006"] would disable every day in March 2006</li>
38944      * <li>["^03"] would disable every day in every March</li>
38945      * </ul>
38946      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38947      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38948      */
38949     disabledDates : null,
38950     /**
38951      * @cfg {String} disabledDatesText
38952      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38953      */
38954     disabledDatesText : "Disabled",
38955     /**
38956      * @cfg {Date/String} minValue
38957      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38958      * valid format (defaults to null).
38959      */
38960     minValue : null,
38961     /**
38962      * @cfg {Date/String} maxValue
38963      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38964      * valid format (defaults to null).
38965      */
38966     maxValue : null,
38967     /**
38968      * @cfg {String} minText
38969      * The error text to display when the date in the cell is before minValue (defaults to
38970      * 'The date in this field must be after {minValue}').
38971      */
38972     minText : "The date in this field must be equal to or after {0}",
38973     /**
38974      * @cfg {String} maxTextf
38975      * The error text to display when the date in the cell is after maxValue (defaults to
38976      * 'The date in this field must be before {maxValue}').
38977      */
38978     maxText : "The date in this field must be equal to or before {0}",
38979     /**
38980      * @cfg {String} invalidText
38981      * The error text to display when the date in the field is invalid (defaults to
38982      * '{value} is not a valid date - it must be in the format {format}').
38983      */
38984     invalidText : "{0} is not a valid date - it must be in the format {1}",
38985     /**
38986      * @cfg {String} triggerClass
38987      * An additional CSS class used to style the trigger button.  The trigger will always get the
38988      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38989      * which displays a calendar icon).
38990      */
38991     triggerClass : 'x-form-date-trigger',
38992     
38993
38994     /**
38995      * @cfg {Boolean} useIso
38996      * if enabled, then the date field will use a hidden field to store the 
38997      * real value as iso formated date. default (true)
38998      */ 
38999     useIso : true,
39000     /**
39001      * @cfg {String/Object} autoCreate
39002      * A DomHelper element spec, or true for a default element spec (defaults to
39003      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39004      */ 
39005     // private
39006     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39007     
39008     // private
39009     hiddenField: false,
39010     
39011     hideMonthPicker : false,
39012     
39013     onRender : function(ct, position)
39014     {
39015         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39016         if (this.useIso) {
39017             this.el.dom.removeAttribute('name'); 
39018             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39019                     'before', true);
39020             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39021             // prevent input submission
39022             this.hiddenName = this.name;
39023         }
39024             
39025             
39026     },
39027     
39028     // private
39029     validateValue : function(value)
39030     {
39031         value = this.formatDate(value);
39032         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39033             return false;
39034         }
39035         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39036              return true;
39037         }
39038         var svalue = value;
39039         value = this.parseDate(value);
39040         if(!value){
39041             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39042             return false;
39043         }
39044         var time = value.getTime();
39045         if(this.minValue && time < this.minValue.getTime()){
39046             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39047             return false;
39048         }
39049         if(this.maxValue && time > this.maxValue.getTime()){
39050             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39051             return false;
39052         }
39053         /*if(this.disabledDays){
39054             var day = value.getDay();
39055             for(var i = 0; i < this.disabledDays.length; i++) {
39056                 if(day === this.disabledDays[i]){
39057                     this.markInvalid(this.disabledDaysText);
39058                     return false;
39059                 }
39060             }
39061         }
39062         */
39063         var fvalue = this.formatDate(value);
39064         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39065             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39066             return false;
39067         }
39068         */
39069         return true;
39070     },
39071
39072     // private
39073     // Provides logic to override the default TriggerField.validateBlur which just returns true
39074     validateBlur : function(){
39075         return !this.menu || !this.menu.isVisible();
39076     },
39077
39078     /**
39079      * Returns the current date value of the date field.
39080      * @return {Date} The date value
39081      */
39082     getValue : function(){
39083         
39084         
39085         
39086         return  this.hiddenField ?
39087                 this.hiddenField.value :
39088                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39089     },
39090
39091     /**
39092      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39093      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39094      * (the default format used is "m/d/y").
39095      * <br />Usage:
39096      * <pre><code>
39097 //All of these calls set the same date value (May 4, 2006)
39098
39099 //Pass a date object:
39100 var dt = new Date('5/4/06');
39101 monthField.setValue(dt);
39102
39103 //Pass a date string (default format):
39104 monthField.setValue('5/4/06');
39105
39106 //Pass a date string (custom format):
39107 monthField.format = 'Y-m-d';
39108 monthField.setValue('2006-5-4');
39109 </code></pre>
39110      * @param {String/Date} date The date or valid date string
39111      */
39112     setValue : function(date){
39113         Roo.log('month setValue' + date);
39114         // can only be first of month..
39115         
39116         var val = this.parseDate(date);
39117         
39118         if (this.hiddenField) {
39119             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39120         }
39121         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39122         this.value = this.parseDate(date);
39123     },
39124
39125     // private
39126     parseDate : function(value){
39127         if(!value || value instanceof Date){
39128             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39129             return value;
39130         }
39131         var v = Date.parseDate(value, this.format);
39132         if (!v && this.useIso) {
39133             v = Date.parseDate(value, 'Y-m-d');
39134         }
39135         if (v) {
39136             // 
39137             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39138         }
39139         
39140         
39141         if(!v && this.altFormats){
39142             if(!this.altFormatsArray){
39143                 this.altFormatsArray = this.altFormats.split("|");
39144             }
39145             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39146                 v = Date.parseDate(value, this.altFormatsArray[i]);
39147             }
39148         }
39149         return v;
39150     },
39151
39152     // private
39153     formatDate : function(date, fmt){
39154         return (!date || !(date instanceof Date)) ?
39155                date : date.dateFormat(fmt || this.format);
39156     },
39157
39158     // private
39159     menuListeners : {
39160         select: function(m, d){
39161             this.setValue(d);
39162             this.fireEvent('select', this, d);
39163         },
39164         show : function(){ // retain focus styling
39165             this.onFocus();
39166         },
39167         hide : function(){
39168             this.focus.defer(10, this);
39169             var ml = this.menuListeners;
39170             this.menu.un("select", ml.select,  this);
39171             this.menu.un("show", ml.show,  this);
39172             this.menu.un("hide", ml.hide,  this);
39173         }
39174     },
39175     // private
39176     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39177     onTriggerClick : function(){
39178         if(this.disabled){
39179             return;
39180         }
39181         if(this.menu == null){
39182             this.menu = new Roo.menu.DateMenu();
39183            
39184         }
39185         
39186         Roo.apply(this.menu.picker,  {
39187             
39188             showClear: this.allowBlank,
39189             minDate : this.minValue,
39190             maxDate : this.maxValue,
39191             disabledDatesRE : this.ddMatch,
39192             disabledDatesText : this.disabledDatesText,
39193             
39194             format : this.useIso ? 'Y-m-d' : this.format,
39195             minText : String.format(this.minText, this.formatDate(this.minValue)),
39196             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39197             
39198         });
39199          this.menu.on(Roo.apply({}, this.menuListeners, {
39200             scope:this
39201         }));
39202        
39203         
39204         var m = this.menu;
39205         var p = m.picker;
39206         
39207         // hide month picker get's called when we called by 'before hide';
39208         
39209         var ignorehide = true;
39210         p.hideMonthPicker  = function(disableAnim){
39211             if (ignorehide) {
39212                 return;
39213             }
39214              if(this.monthPicker){
39215                 Roo.log("hideMonthPicker called");
39216                 if(disableAnim === true){
39217                     this.monthPicker.hide();
39218                 }else{
39219                     this.monthPicker.slideOut('t', {duration:.2});
39220                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39221                     p.fireEvent("select", this, this.value);
39222                     m.hide();
39223                 }
39224             }
39225         }
39226         
39227         Roo.log('picker set value');
39228         Roo.log(this.getValue());
39229         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39230         m.show(this.el, 'tl-bl?');
39231         ignorehide  = false;
39232         // this will trigger hideMonthPicker..
39233         
39234         
39235         // hidden the day picker
39236         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39237         
39238         
39239         
39240       
39241         
39242         p.showMonthPicker.defer(100, p);
39243     
39244         
39245        
39246     },
39247
39248     beforeBlur : function(){
39249         var v = this.parseDate(this.getRawValue());
39250         if(v){
39251             this.setValue(v);
39252         }
39253     }
39254
39255     /** @cfg {Boolean} grow @hide */
39256     /** @cfg {Number} growMin @hide */
39257     /** @cfg {Number} growMax @hide */
39258     /**
39259      * @hide
39260      * @method autoSize
39261      */
39262 });/*
39263  * Based on:
39264  * Ext JS Library 1.1.1
39265  * Copyright(c) 2006-2007, Ext JS, LLC.
39266  *
39267  * Originally Released Under LGPL - original licence link has changed is not relivant.
39268  *
39269  * Fork - LGPL
39270  * <script type="text/javascript">
39271  */
39272  
39273
39274 /**
39275  * @class Roo.form.ComboBox
39276  * @extends Roo.form.TriggerField
39277  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39278  * @constructor
39279  * Create a new ComboBox.
39280  * @param {Object} config Configuration options
39281  */
39282 Roo.form.ComboBox = function(config){
39283     Roo.form.ComboBox.superclass.constructor.call(this, config);
39284     this.addEvents({
39285         /**
39286          * @event expand
39287          * Fires when the dropdown list is expanded
39288              * @param {Roo.form.ComboBox} combo This combo box
39289              */
39290         'expand' : true,
39291         /**
39292          * @event collapse
39293          * Fires when the dropdown list is collapsed
39294              * @param {Roo.form.ComboBox} combo This combo box
39295              */
39296         'collapse' : true,
39297         /**
39298          * @event beforeselect
39299          * Fires before a list item is selected. Return false to cancel the selection.
39300              * @param {Roo.form.ComboBox} combo This combo box
39301              * @param {Roo.data.Record} record The data record returned from the underlying store
39302              * @param {Number} index The index of the selected item in the dropdown list
39303              */
39304         'beforeselect' : true,
39305         /**
39306          * @event select
39307          * Fires when a list item is selected
39308              * @param {Roo.form.ComboBox} combo This combo box
39309              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39310              * @param {Number} index The index of the selected item in the dropdown list
39311              */
39312         'select' : true,
39313         /**
39314          * @event beforequery
39315          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39316          * The event object passed has these properties:
39317              * @param {Roo.form.ComboBox} combo This combo box
39318              * @param {String} query The query
39319              * @param {Boolean} forceAll true to force "all" query
39320              * @param {Boolean} cancel true to cancel the query
39321              * @param {Object} e The query event object
39322              */
39323         'beforequery': true,
39324          /**
39325          * @event add
39326          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39327              * @param {Roo.form.ComboBox} combo This combo box
39328              */
39329         'add' : true,
39330         /**
39331          * @event edit
39332          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39333              * @param {Roo.form.ComboBox} combo This combo box
39334              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39335              */
39336         'edit' : true
39337         
39338         
39339     });
39340     if(this.transform){
39341         this.allowDomMove = false;
39342         var s = Roo.getDom(this.transform);
39343         if(!this.hiddenName){
39344             this.hiddenName = s.name;
39345         }
39346         if(!this.store){
39347             this.mode = 'local';
39348             var d = [], opts = s.options;
39349             for(var i = 0, len = opts.length;i < len; i++){
39350                 var o = opts[i];
39351                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39352                 if(o.selected) {
39353                     this.value = value;
39354                 }
39355                 d.push([value, o.text]);
39356             }
39357             this.store = new Roo.data.SimpleStore({
39358                 'id': 0,
39359                 fields: ['value', 'text'],
39360                 data : d
39361             });
39362             this.valueField = 'value';
39363             this.displayField = 'text';
39364         }
39365         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39366         if(!this.lazyRender){
39367             this.target = true;
39368             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39369             s.parentNode.removeChild(s); // remove it
39370             this.render(this.el.parentNode);
39371         }else{
39372             s.parentNode.removeChild(s); // remove it
39373         }
39374
39375     }
39376     if (this.store) {
39377         this.store = Roo.factory(this.store, Roo.data);
39378     }
39379     
39380     this.selectedIndex = -1;
39381     if(this.mode == 'local'){
39382         if(config.queryDelay === undefined){
39383             this.queryDelay = 10;
39384         }
39385         if(config.minChars === undefined){
39386             this.minChars = 0;
39387         }
39388     }
39389 };
39390
39391 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39392     /**
39393      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39394      */
39395     /**
39396      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39397      * rendering into an Roo.Editor, defaults to false)
39398      */
39399     /**
39400      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39401      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39402      */
39403     /**
39404      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39405      */
39406     /**
39407      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39408      * the dropdown list (defaults to undefined, with no header element)
39409      */
39410
39411      /**
39412      * @cfg {String/Roo.Template} tpl The template to use to render the output
39413      */
39414      
39415     // private
39416     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39417     /**
39418      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39419      */
39420     listWidth: undefined,
39421     /**
39422      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39423      * mode = 'remote' or 'text' if mode = 'local')
39424      */
39425     displayField: undefined,
39426     /**
39427      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39428      * mode = 'remote' or 'value' if mode = 'local'). 
39429      * Note: use of a valueField requires the user make a selection
39430      * in order for a value to be mapped.
39431      */
39432     valueField: undefined,
39433     
39434     
39435     /**
39436      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39437      * field's data value (defaults to the underlying DOM element's name)
39438      */
39439     hiddenName: undefined,
39440     /**
39441      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39442      */
39443     listClass: '',
39444     /**
39445      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39446      */
39447     selectedClass: 'x-combo-selected',
39448     /**
39449      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39450      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39451      * which displays a downward arrow icon).
39452      */
39453     triggerClass : 'x-form-arrow-trigger',
39454     /**
39455      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39456      */
39457     shadow:'sides',
39458     /**
39459      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39460      * anchor positions (defaults to 'tl-bl')
39461      */
39462     listAlign: 'tl-bl?',
39463     /**
39464      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39465      */
39466     maxHeight: 300,
39467     /**
39468      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39469      * query specified by the allQuery config option (defaults to 'query')
39470      */
39471     triggerAction: 'query',
39472     /**
39473      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39474      * (defaults to 4, does not apply if editable = false)
39475      */
39476     minChars : 4,
39477     /**
39478      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39479      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39480      */
39481     typeAhead: false,
39482     /**
39483      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39484      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39485      */
39486     queryDelay: 500,
39487     /**
39488      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39489      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39490      */
39491     pageSize: 0,
39492     /**
39493      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39494      * when editable = true (defaults to false)
39495      */
39496     selectOnFocus:false,
39497     /**
39498      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39499      */
39500     queryParam: 'query',
39501     /**
39502      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39503      * when mode = 'remote' (defaults to 'Loading...')
39504      */
39505     loadingText: 'Loading...',
39506     /**
39507      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39508      */
39509     resizable: false,
39510     /**
39511      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39512      */
39513     handleHeight : 8,
39514     /**
39515      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39516      * traditional select (defaults to true)
39517      */
39518     editable: true,
39519     /**
39520      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39521      */
39522     allQuery: '',
39523     /**
39524      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39525      */
39526     mode: 'remote',
39527     /**
39528      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39529      * listWidth has a higher value)
39530      */
39531     minListWidth : 70,
39532     /**
39533      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39534      * allow the user to set arbitrary text into the field (defaults to false)
39535      */
39536     forceSelection:false,
39537     /**
39538      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39539      * if typeAhead = true (defaults to 250)
39540      */
39541     typeAheadDelay : 250,
39542     /**
39543      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39544      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39545      */
39546     valueNotFoundText : undefined,
39547     /**
39548      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39549      */
39550     blockFocus : false,
39551     
39552     /**
39553      * @cfg {Boolean} disableClear Disable showing of clear button.
39554      */
39555     disableClear : false,
39556     /**
39557      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39558      */
39559     alwaysQuery : false,
39560     
39561     //private
39562     addicon : false,
39563     editicon: false,
39564     
39565     // element that contains real text value.. (when hidden is used..)
39566      
39567     // private
39568     onRender : function(ct, position){
39569         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39570         if(this.hiddenName){
39571             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39572                     'before', true);
39573             this.hiddenField.value =
39574                 this.hiddenValue !== undefined ? this.hiddenValue :
39575                 this.value !== undefined ? this.value : '';
39576
39577             // prevent input submission
39578             this.el.dom.removeAttribute('name');
39579              
39580              
39581         }
39582         if(Roo.isGecko){
39583             this.el.dom.setAttribute('autocomplete', 'off');
39584         }
39585
39586         var cls = 'x-combo-list';
39587
39588         this.list = new Roo.Layer({
39589             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39590         });
39591
39592         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39593         this.list.setWidth(lw);
39594         this.list.swallowEvent('mousewheel');
39595         this.assetHeight = 0;
39596
39597         if(this.title){
39598             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39599             this.assetHeight += this.header.getHeight();
39600         }
39601
39602         this.innerList = this.list.createChild({cls:cls+'-inner'});
39603         this.innerList.on('mouseover', this.onViewOver, this);
39604         this.innerList.on('mousemove', this.onViewMove, this);
39605         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39606         
39607         if(this.allowBlank && !this.pageSize && !this.disableClear){
39608             this.footer = this.list.createChild({cls:cls+'-ft'});
39609             this.pageTb = new Roo.Toolbar(this.footer);
39610            
39611         }
39612         if(this.pageSize){
39613             this.footer = this.list.createChild({cls:cls+'-ft'});
39614             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39615                     {pageSize: this.pageSize});
39616             
39617         }
39618         
39619         if (this.pageTb && this.allowBlank && !this.disableClear) {
39620             var _this = this;
39621             this.pageTb.add(new Roo.Toolbar.Fill(), {
39622                 cls: 'x-btn-icon x-btn-clear',
39623                 text: '&#160;',
39624                 handler: function()
39625                 {
39626                     _this.collapse();
39627                     _this.clearValue();
39628                     _this.onSelect(false, -1);
39629                 }
39630             });
39631         }
39632         if (this.footer) {
39633             this.assetHeight += this.footer.getHeight();
39634         }
39635         
39636
39637         if(!this.tpl){
39638             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39639         }
39640
39641         this.view = new Roo.View(this.innerList, this.tpl, {
39642             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39643         });
39644
39645         this.view.on('click', this.onViewClick, this);
39646
39647         this.store.on('beforeload', this.onBeforeLoad, this);
39648         this.store.on('load', this.onLoad, this);
39649         this.store.on('loadexception', this.onLoadException, this);
39650
39651         if(this.resizable){
39652             this.resizer = new Roo.Resizable(this.list,  {
39653                pinned:true, handles:'se'
39654             });
39655             this.resizer.on('resize', function(r, w, h){
39656                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39657                 this.listWidth = w;
39658                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39659                 this.restrictHeight();
39660             }, this);
39661             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39662         }
39663         if(!this.editable){
39664             this.editable = true;
39665             this.setEditable(false);
39666         }  
39667         
39668         
39669         if (typeof(this.events.add.listeners) != 'undefined') {
39670             
39671             this.addicon = this.wrap.createChild(
39672                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39673        
39674             this.addicon.on('click', function(e) {
39675                 this.fireEvent('add', this);
39676             }, this);
39677         }
39678         if (typeof(this.events.edit.listeners) != 'undefined') {
39679             
39680             this.editicon = this.wrap.createChild(
39681                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39682             if (this.addicon) {
39683                 this.editicon.setStyle('margin-left', '40px');
39684             }
39685             this.editicon.on('click', function(e) {
39686                 
39687                 // we fire even  if inothing is selected..
39688                 this.fireEvent('edit', this, this.lastData );
39689                 
39690             }, this);
39691         }
39692         
39693         
39694         
39695     },
39696
39697     // private
39698     initEvents : function(){
39699         Roo.form.ComboBox.superclass.initEvents.call(this);
39700
39701         this.keyNav = new Roo.KeyNav(this.el, {
39702             "up" : function(e){
39703                 this.inKeyMode = true;
39704                 this.selectPrev();
39705             },
39706
39707             "down" : function(e){
39708                 if(!this.isExpanded()){
39709                     this.onTriggerClick();
39710                 }else{
39711                     this.inKeyMode = true;
39712                     this.selectNext();
39713                 }
39714             },
39715
39716             "enter" : function(e){
39717                 this.onViewClick();
39718                 //return true;
39719             },
39720
39721             "esc" : function(e){
39722                 this.collapse();
39723             },
39724
39725             "tab" : function(e){
39726                 this.onViewClick(false);
39727                 this.fireEvent("specialkey", this, e);
39728                 return true;
39729             },
39730
39731             scope : this,
39732
39733             doRelay : function(foo, bar, hname){
39734                 if(hname == 'down' || this.scope.isExpanded()){
39735                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39736                 }
39737                 return true;
39738             },
39739
39740             forceKeyDown: true
39741         });
39742         this.queryDelay = Math.max(this.queryDelay || 10,
39743                 this.mode == 'local' ? 10 : 250);
39744         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39745         if(this.typeAhead){
39746             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39747         }
39748         if(this.editable !== false){
39749             this.el.on("keyup", this.onKeyUp, this);
39750         }
39751         if(this.forceSelection){
39752             this.on('blur', this.doForce, this);
39753         }
39754     },
39755
39756     onDestroy : function(){
39757         if(this.view){
39758             this.view.setStore(null);
39759             this.view.el.removeAllListeners();
39760             this.view.el.remove();
39761             this.view.purgeListeners();
39762         }
39763         if(this.list){
39764             this.list.destroy();
39765         }
39766         if(this.store){
39767             this.store.un('beforeload', this.onBeforeLoad, this);
39768             this.store.un('load', this.onLoad, this);
39769             this.store.un('loadexception', this.onLoadException, this);
39770         }
39771         Roo.form.ComboBox.superclass.onDestroy.call(this);
39772     },
39773
39774     // private
39775     fireKey : function(e){
39776         if(e.isNavKeyPress() && !this.list.isVisible()){
39777             this.fireEvent("specialkey", this, e);
39778         }
39779     },
39780
39781     // private
39782     onResize: function(w, h){
39783         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39784         
39785         if(typeof w != 'number'){
39786             // we do not handle it!?!?
39787             return;
39788         }
39789         var tw = this.trigger.getWidth();
39790         tw += this.addicon ? this.addicon.getWidth() : 0;
39791         tw += this.editicon ? this.editicon.getWidth() : 0;
39792         var x = w - tw;
39793         this.el.setWidth( this.adjustWidth('input', x));
39794             
39795         this.trigger.setStyle('left', x+'px');
39796         
39797         if(this.list && this.listWidth === undefined){
39798             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39799             this.list.setWidth(lw);
39800             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39801         }
39802         
39803     
39804         
39805     },
39806
39807     /**
39808      * Allow or prevent the user from directly editing the field text.  If false is passed,
39809      * the user will only be able to select from the items defined in the dropdown list.  This method
39810      * is the runtime equivalent of setting the 'editable' config option at config time.
39811      * @param {Boolean} value True to allow the user to directly edit the field text
39812      */
39813     setEditable : function(value){
39814         if(value == this.editable){
39815             return;
39816         }
39817         this.editable = value;
39818         if(!value){
39819             this.el.dom.setAttribute('readOnly', true);
39820             this.el.on('mousedown', this.onTriggerClick,  this);
39821             this.el.addClass('x-combo-noedit');
39822         }else{
39823             this.el.dom.setAttribute('readOnly', false);
39824             this.el.un('mousedown', this.onTriggerClick,  this);
39825             this.el.removeClass('x-combo-noedit');
39826         }
39827     },
39828
39829     // private
39830     onBeforeLoad : function(){
39831         if(!this.hasFocus){
39832             return;
39833         }
39834         this.innerList.update(this.loadingText ?
39835                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39836         this.restrictHeight();
39837         this.selectedIndex = -1;
39838     },
39839
39840     // private
39841     onLoad : function(){
39842         if(!this.hasFocus){
39843             return;
39844         }
39845         if(this.store.getCount() > 0){
39846             this.expand();
39847             this.restrictHeight();
39848             if(this.lastQuery == this.allQuery){
39849                 if(this.editable){
39850                     this.el.dom.select();
39851                 }
39852                 if(!this.selectByValue(this.value, true)){
39853                     this.select(0, true);
39854                 }
39855             }else{
39856                 this.selectNext();
39857                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39858                     this.taTask.delay(this.typeAheadDelay);
39859                 }
39860             }
39861         }else{
39862             this.onEmptyResults();
39863         }
39864         //this.el.focus();
39865     },
39866     // private
39867     onLoadException : function()
39868     {
39869         this.collapse();
39870         Roo.log(this.store.reader.jsonData);
39871         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39872             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39873         }
39874         
39875         
39876     },
39877     // private
39878     onTypeAhead : function(){
39879         if(this.store.getCount() > 0){
39880             var r = this.store.getAt(0);
39881             var newValue = r.data[this.displayField];
39882             var len = newValue.length;
39883             var selStart = this.getRawValue().length;
39884             if(selStart != len){
39885                 this.setRawValue(newValue);
39886                 this.selectText(selStart, newValue.length);
39887             }
39888         }
39889     },
39890
39891     // private
39892     onSelect : function(record, index){
39893         if(this.fireEvent('beforeselect', this, record, index) !== false){
39894             this.setFromData(index > -1 ? record.data : false);
39895             this.collapse();
39896             this.fireEvent('select', this, record, index);
39897         }
39898     },
39899
39900     /**
39901      * Returns the currently selected field value or empty string if no value is set.
39902      * @return {String} value The selected value
39903      */
39904     getValue : function(){
39905         if(this.valueField){
39906             return typeof this.value != 'undefined' ? this.value : '';
39907         }
39908         return Roo.form.ComboBox.superclass.getValue.call(this);
39909     },
39910
39911     /**
39912      * Clears any text/value currently set in the field
39913      */
39914     clearValue : function(){
39915         if(this.hiddenField){
39916             this.hiddenField.value = '';
39917         }
39918         this.value = '';
39919         this.setRawValue('');
39920         this.lastSelectionText = '';
39921         
39922     },
39923
39924     /**
39925      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39926      * will be displayed in the field.  If the value does not match the data value of an existing item,
39927      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39928      * Otherwise the field will be blank (although the value will still be set).
39929      * @param {String} value The value to match
39930      */
39931     setValue : function(v){
39932         var text = v;
39933         if(this.valueField){
39934             var r = this.findRecord(this.valueField, v);
39935             if(r){
39936                 text = r.data[this.displayField];
39937             }else if(this.valueNotFoundText !== undefined){
39938                 text = this.valueNotFoundText;
39939             }
39940         }
39941         this.lastSelectionText = text;
39942         if(this.hiddenField){
39943             this.hiddenField.value = v;
39944         }
39945         Roo.form.ComboBox.superclass.setValue.call(this, text);
39946         this.value = v;
39947     },
39948     /**
39949      * @property {Object} the last set data for the element
39950      */
39951     
39952     lastData : false,
39953     /**
39954      * Sets the value of the field based on a object which is related to the record format for the store.
39955      * @param {Object} value the value to set as. or false on reset?
39956      */
39957     setFromData : function(o){
39958         var dv = ''; // display value
39959         var vv = ''; // value value..
39960         this.lastData = o;
39961         if (this.displayField) {
39962             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39963         } else {
39964             // this is an error condition!!!
39965             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39966         }
39967         
39968         if(this.valueField){
39969             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39970         }
39971         if(this.hiddenField){
39972             this.hiddenField.value = vv;
39973             
39974             this.lastSelectionText = dv;
39975             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39976             this.value = vv;
39977             return;
39978         }
39979         // no hidden field.. - we store the value in 'value', but still display
39980         // display field!!!!
39981         this.lastSelectionText = dv;
39982         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39983         this.value = vv;
39984         
39985         
39986     },
39987     // private
39988     reset : function(){
39989         // overridden so that last data is reset..
39990         this.setValue(this.resetValue);
39991         this.clearInvalid();
39992         this.lastData = false;
39993         if (this.view) {
39994             this.view.clearSelections();
39995         }
39996     },
39997     // private
39998     findRecord : function(prop, value){
39999         var record;
40000         if(this.store.getCount() > 0){
40001             this.store.each(function(r){
40002                 if(r.data[prop] == value){
40003                     record = r;
40004                     return false;
40005                 }
40006                 return true;
40007             });
40008         }
40009         return record;
40010     },
40011     
40012     getName: function()
40013     {
40014         // returns hidden if it's set..
40015         if (!this.rendered) {return ''};
40016         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40017         
40018     },
40019     // private
40020     onViewMove : function(e, t){
40021         this.inKeyMode = false;
40022     },
40023
40024     // private
40025     onViewOver : function(e, t){
40026         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40027             return;
40028         }
40029         var item = this.view.findItemFromChild(t);
40030         if(item){
40031             var index = this.view.indexOf(item);
40032             this.select(index, false);
40033         }
40034     },
40035
40036     // private
40037     onViewClick : function(doFocus)
40038     {
40039         var index = this.view.getSelectedIndexes()[0];
40040         var r = this.store.getAt(index);
40041         if(r){
40042             this.onSelect(r, index);
40043         }
40044         if(doFocus !== false && !this.blockFocus){
40045             this.el.focus();
40046         }
40047     },
40048
40049     // private
40050     restrictHeight : function(){
40051         this.innerList.dom.style.height = '';
40052         var inner = this.innerList.dom;
40053         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40054         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40055         this.list.beginUpdate();
40056         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40057         this.list.alignTo(this.el, this.listAlign);
40058         this.list.endUpdate();
40059     },
40060
40061     // private
40062     onEmptyResults : function(){
40063         this.collapse();
40064     },
40065
40066     /**
40067      * Returns true if the dropdown list is expanded, else false.
40068      */
40069     isExpanded : function(){
40070         return this.list.isVisible();
40071     },
40072
40073     /**
40074      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40075      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40076      * @param {String} value The data value of the item to select
40077      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40078      * selected item if it is not currently in view (defaults to true)
40079      * @return {Boolean} True if the value matched an item in the list, else false
40080      */
40081     selectByValue : function(v, scrollIntoView){
40082         if(v !== undefined && v !== null){
40083             var r = this.findRecord(this.valueField || this.displayField, v);
40084             if(r){
40085                 this.select(this.store.indexOf(r), scrollIntoView);
40086                 return true;
40087             }
40088         }
40089         return false;
40090     },
40091
40092     /**
40093      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40094      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40095      * @param {Number} index The zero-based index of the list item to select
40096      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40097      * selected item if it is not currently in view (defaults to true)
40098      */
40099     select : function(index, scrollIntoView){
40100         this.selectedIndex = index;
40101         this.view.select(index);
40102         if(scrollIntoView !== false){
40103             var el = this.view.getNode(index);
40104             if(el){
40105                 this.innerList.scrollChildIntoView(el, false);
40106             }
40107         }
40108     },
40109
40110     // private
40111     selectNext : function(){
40112         var ct = this.store.getCount();
40113         if(ct > 0){
40114             if(this.selectedIndex == -1){
40115                 this.select(0);
40116             }else if(this.selectedIndex < ct-1){
40117                 this.select(this.selectedIndex+1);
40118             }
40119         }
40120     },
40121
40122     // private
40123     selectPrev : function(){
40124         var ct = this.store.getCount();
40125         if(ct > 0){
40126             if(this.selectedIndex == -1){
40127                 this.select(0);
40128             }else if(this.selectedIndex != 0){
40129                 this.select(this.selectedIndex-1);
40130             }
40131         }
40132     },
40133
40134     // private
40135     onKeyUp : function(e){
40136         if(this.editable !== false && !e.isSpecialKey()){
40137             this.lastKey = e.getKey();
40138             this.dqTask.delay(this.queryDelay);
40139         }
40140     },
40141
40142     // private
40143     validateBlur : function(){
40144         return !this.list || !this.list.isVisible();   
40145     },
40146
40147     // private
40148     initQuery : function(){
40149         this.doQuery(this.getRawValue());
40150     },
40151
40152     // private
40153     doForce : function(){
40154         if(this.el.dom.value.length > 0){
40155             this.el.dom.value =
40156                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40157              
40158         }
40159     },
40160
40161     /**
40162      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40163      * query allowing the query action to be canceled if needed.
40164      * @param {String} query The SQL query to execute
40165      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40166      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40167      * saved in the current store (defaults to false)
40168      */
40169     doQuery : function(q, forceAll){
40170         if(q === undefined || q === null){
40171             q = '';
40172         }
40173         var qe = {
40174             query: q,
40175             forceAll: forceAll,
40176             combo: this,
40177             cancel:false
40178         };
40179         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40180             return false;
40181         }
40182         q = qe.query;
40183         forceAll = qe.forceAll;
40184         if(forceAll === true || (q.length >= this.minChars)){
40185             if(this.lastQuery != q || this.alwaysQuery){
40186                 this.lastQuery = q;
40187                 if(this.mode == 'local'){
40188                     this.selectedIndex = -1;
40189                     if(forceAll){
40190                         this.store.clearFilter();
40191                     }else{
40192                         this.store.filter(this.displayField, q);
40193                     }
40194                     this.onLoad();
40195                 }else{
40196                     this.store.baseParams[this.queryParam] = q;
40197                     this.store.load({
40198                         params: this.getParams(q)
40199                     });
40200                     this.expand();
40201                 }
40202             }else{
40203                 this.selectedIndex = -1;
40204                 this.onLoad();   
40205             }
40206         }
40207     },
40208
40209     // private
40210     getParams : function(q){
40211         var p = {};
40212         //p[this.queryParam] = q;
40213         if(this.pageSize){
40214             p.start = 0;
40215             p.limit = this.pageSize;
40216         }
40217         return p;
40218     },
40219
40220     /**
40221      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40222      */
40223     collapse : function(){
40224         if(!this.isExpanded()){
40225             return;
40226         }
40227         this.list.hide();
40228         Roo.get(document).un('mousedown', this.collapseIf, this);
40229         Roo.get(document).un('mousewheel', this.collapseIf, this);
40230         if (!this.editable) {
40231             Roo.get(document).un('keydown', this.listKeyPress, this);
40232         }
40233         this.fireEvent('collapse', this);
40234     },
40235
40236     // private
40237     collapseIf : function(e){
40238         if(!e.within(this.wrap) && !e.within(this.list)){
40239             this.collapse();
40240         }
40241     },
40242
40243     /**
40244      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40245      */
40246     expand : function(){
40247         if(this.isExpanded() || !this.hasFocus){
40248             return;
40249         }
40250         this.list.alignTo(this.el, this.listAlign);
40251         this.list.show();
40252         Roo.get(document).on('mousedown', this.collapseIf, this);
40253         Roo.get(document).on('mousewheel', this.collapseIf, this);
40254         if (!this.editable) {
40255             Roo.get(document).on('keydown', this.listKeyPress, this);
40256         }
40257         
40258         this.fireEvent('expand', this);
40259     },
40260
40261     // private
40262     // Implements the default empty TriggerField.onTriggerClick function
40263     onTriggerClick : function(){
40264         if(this.disabled){
40265             return;
40266         }
40267         if(this.isExpanded()){
40268             this.collapse();
40269             if (!this.blockFocus) {
40270                 this.el.focus();
40271             }
40272             
40273         }else {
40274             this.hasFocus = true;
40275             if(this.triggerAction == 'all') {
40276                 this.doQuery(this.allQuery, true);
40277             } else {
40278                 this.doQuery(this.getRawValue());
40279             }
40280             if (!this.blockFocus) {
40281                 this.el.focus();
40282             }
40283         }
40284     },
40285     listKeyPress : function(e)
40286     {
40287         //Roo.log('listkeypress');
40288         // scroll to first matching element based on key pres..
40289         if (e.isSpecialKey()) {
40290             return false;
40291         }
40292         var k = String.fromCharCode(e.getKey()).toUpperCase();
40293         //Roo.log(k);
40294         var match  = false;
40295         var csel = this.view.getSelectedNodes();
40296         var cselitem = false;
40297         if (csel.length) {
40298             var ix = this.view.indexOf(csel[0]);
40299             cselitem  = this.store.getAt(ix);
40300             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40301                 cselitem = false;
40302             }
40303             
40304         }
40305         
40306         this.store.each(function(v) { 
40307             if (cselitem) {
40308                 // start at existing selection.
40309                 if (cselitem.id == v.id) {
40310                     cselitem = false;
40311                 }
40312                 return;
40313             }
40314                 
40315             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40316                 match = this.store.indexOf(v);
40317                 return false;
40318             }
40319         }, this);
40320         
40321         if (match === false) {
40322             return true; // no more action?
40323         }
40324         // scroll to?
40325         this.view.select(match);
40326         var sn = Roo.get(this.view.getSelectedNodes()[0])
40327         sn.scrollIntoView(sn.dom.parentNode, false);
40328     }
40329
40330     /** 
40331     * @cfg {Boolean} grow 
40332     * @hide 
40333     */
40334     /** 
40335     * @cfg {Number} growMin 
40336     * @hide 
40337     */
40338     /** 
40339     * @cfg {Number} growMax 
40340     * @hide 
40341     */
40342     /**
40343      * @hide
40344      * @method autoSize
40345      */
40346 });/*
40347  * Copyright(c) 2010-2012, Roo J Solutions Limited
40348  *
40349  * Licence LGPL
40350  *
40351  */
40352
40353 /**
40354  * @class Roo.form.ComboBoxArray
40355  * @extends Roo.form.TextField
40356  * A facebook style adder... for lists of email / people / countries  etc...
40357  * pick multiple items from a combo box, and shows each one.
40358  *
40359  *  Fred [x]  Brian [x]  [Pick another |v]
40360  *
40361  *
40362  *  For this to work: it needs various extra information
40363  *    - normal combo problay has
40364  *      name, hiddenName
40365  *    + displayField, valueField
40366  *
40367  *    For our purpose...
40368  *
40369  *
40370  *   If we change from 'extends' to wrapping...
40371  *   
40372  *  
40373  *
40374  
40375  
40376  * @constructor
40377  * Create a new ComboBoxArray.
40378  * @param {Object} config Configuration options
40379  */
40380  
40381
40382 Roo.form.ComboBoxArray = function(config)
40383 {
40384     this.addEvents({
40385         /**
40386          * @event beforeremove
40387          * Fires before remove the value from the list
40388              * @param {Roo.form.ComboBoxArray} _self This combo box array
40389              * @param {Roo.form.ComboBoxArray.Item} item removed item
40390              */
40391         'beforeremove' : true,
40392         /**
40393          * @event remove
40394          * Fires when remove the value from the list
40395              * @param {Roo.form.ComboBoxArray} _self This combo box array
40396              * @param {Roo.form.ComboBoxArray.Item} item removed item
40397              */
40398         'remove' : true
40399         
40400         
40401     });
40402     
40403     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40404     
40405     this.items = new Roo.util.MixedCollection(false);
40406     
40407     // construct the child combo...
40408     
40409     
40410     
40411     
40412    
40413     
40414 }
40415
40416  
40417 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40418
40419     /**
40420      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40421      */
40422     
40423     lastData : false,
40424     
40425     // behavies liek a hiddne field
40426     inputType:      'hidden',
40427     /**
40428      * @cfg {Number} width The width of the box that displays the selected element
40429      */ 
40430     width:          300,
40431
40432     
40433     
40434     /**
40435      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40436      */
40437     name : false,
40438     /**
40439      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40440      */
40441     hiddenName : false,
40442     
40443     
40444     // private the array of items that are displayed..
40445     items  : false,
40446     // private - the hidden field el.
40447     hiddenEl : false,
40448     // private - the filed el..
40449     el : false,
40450     
40451     //validateValue : function() { return true; }, // all values are ok!
40452     //onAddClick: function() { },
40453     
40454     onRender : function(ct, position) 
40455     {
40456         
40457         // create the standard hidden element
40458         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40459         
40460         
40461         // give fake names to child combo;
40462         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40463         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40464         
40465         this.combo = Roo.factory(this.combo, Roo.form);
40466         this.combo.onRender(ct, position);
40467         if (typeof(this.combo.width) != 'undefined') {
40468             this.combo.onResize(this.combo.width,0);
40469         }
40470         
40471         this.combo.initEvents();
40472         
40473         // assigned so form know we need to do this..
40474         this.store          = this.combo.store;
40475         this.valueField     = this.combo.valueField;
40476         this.displayField   = this.combo.displayField ;
40477         
40478         
40479         this.combo.wrap.addClass('x-cbarray-grp');
40480         
40481         var cbwrap = this.combo.wrap.createChild(
40482             {tag: 'div', cls: 'x-cbarray-cb'},
40483             this.combo.el.dom
40484         );
40485         
40486              
40487         this.hiddenEl = this.combo.wrap.createChild({
40488             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40489         });
40490         this.el = this.combo.wrap.createChild({
40491             tag: 'input',  type:'hidden' , name: this.name, value : ''
40492         });
40493          //   this.el.dom.removeAttribute("name");
40494         
40495         
40496         this.outerWrap = this.combo.wrap;
40497         this.wrap = cbwrap;
40498         
40499         this.outerWrap.setWidth(this.width);
40500         this.outerWrap.dom.removeChild(this.el.dom);
40501         
40502         this.wrap.dom.appendChild(this.el.dom);
40503         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40504         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40505         
40506         this.combo.trigger.setStyle('position','relative');
40507         this.combo.trigger.setStyle('left', '0px');
40508         this.combo.trigger.setStyle('top', '2px');
40509         
40510         this.combo.el.setStyle('vertical-align', 'text-bottom');
40511         
40512         //this.trigger.setStyle('vertical-align', 'top');
40513         
40514         // this should use the code from combo really... on('add' ....)
40515         if (this.adder) {
40516             
40517         
40518             this.adder = this.outerWrap.createChild(
40519                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40520             var _t = this;
40521             this.adder.on('click', function(e) {
40522                 _t.fireEvent('adderclick', this, e);
40523             }, _t);
40524         }
40525         //var _t = this;
40526         //this.adder.on('click', this.onAddClick, _t);
40527         
40528         
40529         this.combo.on('select', function(cb, rec, ix) {
40530             this.addItem(rec.data);
40531             
40532             cb.setValue('');
40533             cb.el.dom.value = '';
40534             //cb.lastData = rec.data;
40535             // add to list
40536             
40537         }, this);
40538         
40539         
40540     },
40541     
40542     
40543     getName: function()
40544     {
40545         // returns hidden if it's set..
40546         if (!this.rendered) {return ''};
40547         return  this.hiddenName ? this.hiddenName : this.name;
40548         
40549     },
40550     
40551     
40552     onResize: function(w, h){
40553         
40554         return;
40555         // not sure if this is needed..
40556         //this.combo.onResize(w,h);
40557         
40558         if(typeof w != 'number'){
40559             // we do not handle it!?!?
40560             return;
40561         }
40562         var tw = this.combo.trigger.getWidth();
40563         tw += this.addicon ? this.addicon.getWidth() : 0;
40564         tw += this.editicon ? this.editicon.getWidth() : 0;
40565         var x = w - tw;
40566         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40567             
40568         this.combo.trigger.setStyle('left', '0px');
40569         
40570         if(this.list && this.listWidth === undefined){
40571             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40572             this.list.setWidth(lw);
40573             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40574         }
40575         
40576     
40577         
40578     },
40579     
40580     addItem: function(rec)
40581     {
40582         var valueField = this.combo.valueField;
40583         var displayField = this.combo.displayField;
40584         if (this.items.indexOfKey(rec[valueField]) > -1) {
40585             //console.log("GOT " + rec.data.id);
40586             return;
40587         }
40588         
40589         var x = new Roo.form.ComboBoxArray.Item({
40590             //id : rec[this.idField],
40591             data : rec,
40592             displayField : displayField ,
40593             tipField : displayField ,
40594             cb : this
40595         });
40596         // use the 
40597         this.items.add(rec[valueField],x);
40598         // add it before the element..
40599         this.updateHiddenEl();
40600         x.render(this.outerWrap, this.wrap.dom);
40601         // add the image handler..
40602     },
40603     
40604     updateHiddenEl : function()
40605     {
40606         this.validate();
40607         if (!this.hiddenEl) {
40608             return;
40609         }
40610         var ar = [];
40611         var idField = this.combo.valueField;
40612         
40613         this.items.each(function(f) {
40614             ar.push(f.data[idField]);
40615            
40616         });
40617         this.hiddenEl.dom.value = ar.join(',');
40618         this.validate();
40619     },
40620     
40621     reset : function()
40622     {
40623         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40624         this.items.each(function(f) {
40625            f.remove(); 
40626         });
40627         this.el.dom.value = '';
40628         if (this.hiddenEl) {
40629             this.hiddenEl.dom.value = '';
40630         }
40631         
40632     },
40633     getValue: function()
40634     {
40635         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40636     },
40637     setValue: function(v) // not a valid action - must use addItems..
40638     {
40639          
40640         this.reset();
40641         
40642         
40643         
40644         if (this.store.isLocal && (typeof(v) == 'string')) {
40645             // then we can use the store to find the values..
40646             // comma seperated at present.. this needs to allow JSON based encoding..
40647             this.hiddenEl.value  = v;
40648             var v_ar = [];
40649             Roo.each(v.split(','), function(k) {
40650                 Roo.log("CHECK " + this.valueField + ',' + k);
40651                 var li = this.store.query(this.valueField, k);
40652                 if (!li.length) {
40653                     return;
40654                 }
40655                 var add = {};
40656                 add[this.valueField] = k;
40657                 add[this.displayField] = li.item(0).data[this.displayField];
40658                 
40659                 this.addItem(add);
40660             }, this) 
40661              
40662         }
40663         if (typeof(v) == 'object' ) {
40664             // then let's assume it's an array of objects..
40665             Roo.each(v, function(l) {
40666                 this.addItem(l);
40667             }, this);
40668              
40669         }
40670         
40671         
40672     },
40673     setFromData: function(v)
40674     {
40675         // this recieves an object, if setValues is called.
40676         this.reset();
40677         this.el.dom.value = v[this.displayField];
40678         this.hiddenEl.dom.value = v[this.valueField];
40679         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40680             return;
40681         }
40682         var kv = v[this.valueField];
40683         var dv = v[this.displayField];
40684         kv = typeof(kv) != 'string' ? '' : kv;
40685         dv = typeof(dv) != 'string' ? '' : dv;
40686         
40687         
40688         var keys = kv.split(',');
40689         var display = dv.split(',');
40690         for (var i = 0 ; i < keys.length; i++) {
40691             
40692             add = {};
40693             add[this.valueField] = keys[i];
40694             add[this.displayField] = display[i];
40695             this.addItem(add);
40696         }
40697       
40698         
40699     },
40700     
40701     /**
40702      * Validates the combox array value
40703      * @return {Boolean} True if the value is valid, else false
40704      */
40705     validate : function(){
40706         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40707             this.clearInvalid();
40708             return true;
40709         }
40710         return false;
40711     },
40712     
40713     validateValue : function(value){
40714         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40715         
40716     },
40717     
40718     /*@
40719      * overide
40720      * 
40721      */
40722     isDirty : function() {
40723         if(this.disabled) {
40724             return false;
40725         }
40726         
40727         try {
40728             var d = Roo.decode(String(this.originalValue));
40729         } catch (e) {
40730             return String(this.getValue()) !== String(this.originalValue);
40731         }
40732         
40733         var originalValue = [];
40734         
40735         for (var i = 0; i < d.length; i++){
40736             originalValue.push(d[i][this.valueField]);
40737         }
40738         
40739         return String(this.getValue()) !== String(originalValue.join(','));
40740         
40741     }
40742     
40743 });
40744
40745
40746
40747 /**
40748  * @class Roo.form.ComboBoxArray.Item
40749  * @extends Roo.BoxComponent
40750  * A selected item in the list
40751  *  Fred [x]  Brian [x]  [Pick another |v]
40752  * 
40753  * @constructor
40754  * Create a new item.
40755  * @param {Object} config Configuration options
40756  */
40757  
40758 Roo.form.ComboBoxArray.Item = function(config) {
40759     config.id = Roo.id();
40760     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40761 }
40762
40763 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40764     data : {},
40765     cb: false,
40766     displayField : false,
40767     tipField : false,
40768     
40769     
40770     defaultAutoCreate : {
40771         tag: 'div',
40772         cls: 'x-cbarray-item',
40773         cn : [ 
40774             { tag: 'div' },
40775             {
40776                 tag: 'img',
40777                 width:16,
40778                 height : 16,
40779                 src : Roo.BLANK_IMAGE_URL ,
40780                 align: 'center'
40781             }
40782         ]
40783         
40784     },
40785     
40786  
40787     onRender : function(ct, position)
40788     {
40789         Roo.form.Field.superclass.onRender.call(this, ct, position);
40790         
40791         if(!this.el){
40792             var cfg = this.getAutoCreate();
40793             this.el = ct.createChild(cfg, position);
40794         }
40795         
40796         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40797         
40798         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40799             this.cb.renderer(this.data) :
40800             String.format('{0}',this.data[this.displayField]);
40801         
40802             
40803         this.el.child('div').dom.setAttribute('qtip',
40804                         String.format('{0}',this.data[this.tipField])
40805         );
40806         
40807         this.el.child('img').on('click', this.remove, this);
40808         
40809     },
40810    
40811     remove : function()
40812     {
40813         if(this.cb.disabled){
40814             return;
40815         }
40816         
40817         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40818             this.cb.items.remove(this);
40819             this.el.child('img').un('click', this.remove, this);
40820             this.el.remove();
40821             this.cb.updateHiddenEl();
40822
40823             this.cb.fireEvent('remove', this.cb, this);
40824         }
40825         
40826     }
40827 });/*
40828  * Based on:
40829  * Ext JS Library 1.1.1
40830  * Copyright(c) 2006-2007, Ext JS, LLC.
40831  *
40832  * Originally Released Under LGPL - original licence link has changed is not relivant.
40833  *
40834  * Fork - LGPL
40835  * <script type="text/javascript">
40836  */
40837 /**
40838  * @class Roo.form.Checkbox
40839  * @extends Roo.form.Field
40840  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40841  * @constructor
40842  * Creates a new Checkbox
40843  * @param {Object} config Configuration options
40844  */
40845 Roo.form.Checkbox = function(config){
40846     Roo.form.Checkbox.superclass.constructor.call(this, config);
40847     this.addEvents({
40848         /**
40849          * @event check
40850          * Fires when the checkbox is checked or unchecked.
40851              * @param {Roo.form.Checkbox} this This checkbox
40852              * @param {Boolean} checked The new checked value
40853              */
40854         check : true
40855     });
40856 };
40857
40858 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40859     /**
40860      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40861      */
40862     focusClass : undefined,
40863     /**
40864      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40865      */
40866     fieldClass: "x-form-field",
40867     /**
40868      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40869      */
40870     checked: false,
40871     /**
40872      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40873      * {tag: "input", type: "checkbox", autocomplete: "off"})
40874      */
40875     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40876     /**
40877      * @cfg {String} boxLabel The text that appears beside the checkbox
40878      */
40879     boxLabel : "",
40880     /**
40881      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40882      */  
40883     inputValue : '1',
40884     /**
40885      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40886      */
40887      valueOff: '0', // value when not checked..
40888
40889     actionMode : 'viewEl', 
40890     //
40891     // private
40892     itemCls : 'x-menu-check-item x-form-item',
40893     groupClass : 'x-menu-group-item',
40894     inputType : 'hidden',
40895     
40896     
40897     inSetChecked: false, // check that we are not calling self...
40898     
40899     inputElement: false, // real input element?
40900     basedOn: false, // ????
40901     
40902     isFormField: true, // not sure where this is needed!!!!
40903
40904     onResize : function(){
40905         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40906         if(!this.boxLabel){
40907             this.el.alignTo(this.wrap, 'c-c');
40908         }
40909     },
40910
40911     initEvents : function(){
40912         Roo.form.Checkbox.superclass.initEvents.call(this);
40913         this.el.on("click", this.onClick,  this);
40914         this.el.on("change", this.onClick,  this);
40915     },
40916
40917
40918     getResizeEl : function(){
40919         return this.wrap;
40920     },
40921
40922     getPositionEl : function(){
40923         return this.wrap;
40924     },
40925
40926     // private
40927     onRender : function(ct, position){
40928         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40929         /*
40930         if(this.inputValue !== undefined){
40931             this.el.dom.value = this.inputValue;
40932         }
40933         */
40934         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40935         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40936         var viewEl = this.wrap.createChild({ 
40937             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40938         this.viewEl = viewEl;   
40939         this.wrap.on('click', this.onClick,  this); 
40940         
40941         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40942         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40943         
40944         
40945         
40946         if(this.boxLabel){
40947             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40948         //    viewEl.on('click', this.onClick,  this); 
40949         }
40950         //if(this.checked){
40951             this.setChecked(this.checked);
40952         //}else{
40953             //this.checked = this.el.dom;
40954         //}
40955
40956     },
40957
40958     // private
40959     initValue : Roo.emptyFn,
40960
40961     /**
40962      * Returns the checked state of the checkbox.
40963      * @return {Boolean} True if checked, else false
40964      */
40965     getValue : function(){
40966         if(this.el){
40967             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40968         }
40969         return this.valueOff;
40970         
40971     },
40972
40973         // private
40974     onClick : function(){ 
40975         if (this.disabled) {
40976             return;
40977         }
40978         this.setChecked(!this.checked);
40979
40980         //if(this.el.dom.checked != this.checked){
40981         //    this.setValue(this.el.dom.checked);
40982        // }
40983     },
40984
40985     /**
40986      * Sets the checked state of the checkbox.
40987      * On is always based on a string comparison between inputValue and the param.
40988      * @param {Boolean/String} value - the value to set 
40989      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40990      */
40991     setValue : function(v,suppressEvent){
40992         
40993         
40994         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40995         //if(this.el && this.el.dom){
40996         //    this.el.dom.checked = this.checked;
40997         //    this.el.dom.defaultChecked = this.checked;
40998         //}
40999         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
41000         //this.fireEvent("check", this, this.checked);
41001     },
41002     // private..
41003     setChecked : function(state,suppressEvent)
41004     {
41005         if (this.inSetChecked) {
41006             this.checked = state;
41007             return;
41008         }
41009         
41010     
41011         if(this.wrap){
41012             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41013         }
41014         this.checked = state;
41015         if(suppressEvent !== true){
41016             this.fireEvent('check', this, state);
41017         }
41018         this.inSetChecked = true;
41019         this.el.dom.value = state ? this.inputValue : this.valueOff;
41020         this.inSetChecked = false;
41021         
41022     },
41023     // handle setting of hidden value by some other method!!?!?
41024     setFromHidden: function()
41025     {
41026         if(!this.el){
41027             return;
41028         }
41029         //console.log("SET FROM HIDDEN");
41030         //alert('setFrom hidden');
41031         this.setValue(this.el.dom.value);
41032     },
41033     
41034     onDestroy : function()
41035     {
41036         if(this.viewEl){
41037             Roo.get(this.viewEl).remove();
41038         }
41039          
41040         Roo.form.Checkbox.superclass.onDestroy.call(this);
41041     }
41042
41043 });/*
41044  * Based on:
41045  * Ext JS Library 1.1.1
41046  * Copyright(c) 2006-2007, Ext JS, LLC.
41047  *
41048  * Originally Released Under LGPL - original licence link has changed is not relivant.
41049  *
41050  * Fork - LGPL
41051  * <script type="text/javascript">
41052  */
41053  
41054 /**
41055  * @class Roo.form.Radio
41056  * @extends Roo.form.Checkbox
41057  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41058  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41059  * @constructor
41060  * Creates a new Radio
41061  * @param {Object} config Configuration options
41062  */
41063 Roo.form.Radio = function(){
41064     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41065 };
41066 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41067     inputType: 'radio',
41068
41069     /**
41070      * If this radio is part of a group, it will return the selected value
41071      * @return {String}
41072      */
41073     getGroupValue : function(){
41074         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41075     },
41076     
41077     
41078     onRender : function(ct, position){
41079         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41080         
41081         if(this.inputValue !== undefined){
41082             this.el.dom.value = this.inputValue;
41083         }
41084          
41085         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41086         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41087         //var viewEl = this.wrap.createChild({ 
41088         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41089         //this.viewEl = viewEl;   
41090         //this.wrap.on('click', this.onClick,  this); 
41091         
41092         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41093         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41094         
41095         
41096         
41097         if(this.boxLabel){
41098             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41099         //    viewEl.on('click', this.onClick,  this); 
41100         }
41101          if(this.checked){
41102             this.el.dom.checked =   'checked' ;
41103         }
41104          
41105     } 
41106     
41107     
41108 });//<script type="text/javascript">
41109
41110 /*
41111  * Based  Ext JS Library 1.1.1
41112  * Copyright(c) 2006-2007, Ext JS, LLC.
41113  * LGPL
41114  *
41115  */
41116  
41117 /**
41118  * @class Roo.HtmlEditorCore
41119  * @extends Roo.Component
41120  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41121  *
41122  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41123  */
41124
41125 Roo.HtmlEditorCore = function(config){
41126     
41127     
41128     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41129     
41130     
41131     this.addEvents({
41132         /**
41133          * @event initialize
41134          * Fires when the editor is fully initialized (including the iframe)
41135          * @param {Roo.HtmlEditorCore} this
41136          */
41137         initialize: true,
41138         /**
41139          * @event activate
41140          * Fires when the editor is first receives the focus. Any insertion must wait
41141          * until after this event.
41142          * @param {Roo.HtmlEditorCore} this
41143          */
41144         activate: true,
41145          /**
41146          * @event beforesync
41147          * Fires before the textarea is updated with content from the editor iframe. Return false
41148          * to cancel the sync.
41149          * @param {Roo.HtmlEditorCore} this
41150          * @param {String} html
41151          */
41152         beforesync: true,
41153          /**
41154          * @event beforepush
41155          * Fires before the iframe editor is updated with content from the textarea. Return false
41156          * to cancel the push.
41157          * @param {Roo.HtmlEditorCore} this
41158          * @param {String} html
41159          */
41160         beforepush: true,
41161          /**
41162          * @event sync
41163          * Fires when the textarea is updated with content from the editor iframe.
41164          * @param {Roo.HtmlEditorCore} this
41165          * @param {String} html
41166          */
41167         sync: true,
41168          /**
41169          * @event push
41170          * Fires when the iframe editor is updated with content from the textarea.
41171          * @param {Roo.HtmlEditorCore} this
41172          * @param {String} html
41173          */
41174         push: true,
41175         
41176         /**
41177          * @event editorevent
41178          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41179          * @param {Roo.HtmlEditorCore} this
41180          */
41181         editorevent: true
41182         
41183     });
41184     
41185     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41186     
41187     // defaults : white / black...
41188     this.applyBlacklists();
41189     
41190     
41191     
41192 };
41193
41194
41195 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41196
41197
41198      /**
41199      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41200      */
41201     
41202     owner : false,
41203     
41204      /**
41205      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41206      *                        Roo.resizable.
41207      */
41208     resizable : false,
41209      /**
41210      * @cfg {Number} height (in pixels)
41211      */   
41212     height: 300,
41213    /**
41214      * @cfg {Number} width (in pixels)
41215      */   
41216     width: 500,
41217     
41218     /**
41219      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41220      * 
41221      */
41222     stylesheets: false,
41223     
41224     // id of frame..
41225     frameId: false,
41226     
41227     // private properties
41228     validationEvent : false,
41229     deferHeight: true,
41230     initialized : false,
41231     activated : false,
41232     sourceEditMode : false,
41233     onFocus : Roo.emptyFn,
41234     iframePad:3,
41235     hideMode:'offsets',
41236     
41237     clearUp: true,
41238     
41239     // blacklist + whitelisted elements..
41240     black: false,
41241     white: false,
41242      
41243     
41244
41245     /**
41246      * Protected method that will not generally be called directly. It
41247      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41248      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41249      */
41250     getDocMarkup : function(){
41251         // body styles..
41252         var st = '';
41253         
41254         // inherit styels from page...?? 
41255         if (this.stylesheets === false) {
41256             
41257             Roo.get(document.head).select('style').each(function(node) {
41258                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41259             });
41260             
41261             Roo.get(document.head).select('link').each(function(node) { 
41262                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41263             });
41264             
41265         } else if (!this.stylesheets.length) {
41266                 // simple..
41267                 st = '<style type="text/css">' +
41268                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41269                    '</style>';
41270         } else { 
41271             
41272         }
41273         
41274         st +=  '<style type="text/css">' +
41275             'IMG { cursor: pointer } ' +
41276         '</style>';
41277
41278         
41279         return '<html><head>' + st  +
41280             //<style type="text/css">' +
41281             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41282             //'</style>' +
41283             ' </head><body class="roo-htmleditor-body"></body></html>';
41284     },
41285
41286     // private
41287     onRender : function(ct, position)
41288     {
41289         var _t = this;
41290         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41291         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41292         
41293         
41294         this.el.dom.style.border = '0 none';
41295         this.el.dom.setAttribute('tabIndex', -1);
41296         this.el.addClass('x-hidden hide');
41297         
41298         
41299         
41300         if(Roo.isIE){ // fix IE 1px bogus margin
41301             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41302         }
41303        
41304         
41305         this.frameId = Roo.id();
41306         
41307          
41308         
41309         var iframe = this.owner.wrap.createChild({
41310             tag: 'iframe',
41311             cls: 'form-control', // bootstrap..
41312             id: this.frameId,
41313             name: this.frameId,
41314             frameBorder : 'no',
41315             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41316         }, this.el
41317         );
41318         
41319         
41320         this.iframe = iframe.dom;
41321
41322          this.assignDocWin();
41323         
41324         this.doc.designMode = 'on';
41325        
41326         this.doc.open();
41327         this.doc.write(this.getDocMarkup());
41328         this.doc.close();
41329
41330         
41331         var task = { // must defer to wait for browser to be ready
41332             run : function(){
41333                 //console.log("run task?" + this.doc.readyState);
41334                 this.assignDocWin();
41335                 if(this.doc.body || this.doc.readyState == 'complete'){
41336                     try {
41337                         this.doc.designMode="on";
41338                     } catch (e) {
41339                         return;
41340                     }
41341                     Roo.TaskMgr.stop(task);
41342                     this.initEditor.defer(10, this);
41343                 }
41344             },
41345             interval : 10,
41346             duration: 10000,
41347             scope: this
41348         };
41349         Roo.TaskMgr.start(task);
41350
41351     },
41352
41353     // private
41354     onResize : function(w, h)
41355     {
41356          Roo.log('resize: ' +w + ',' + h );
41357         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41358         if(!this.iframe){
41359             return;
41360         }
41361         if(typeof w == 'number'){
41362             
41363             this.iframe.style.width = w + 'px';
41364         }
41365         if(typeof h == 'number'){
41366             
41367             this.iframe.style.height = h + 'px';
41368             if(this.doc){
41369                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41370             }
41371         }
41372         
41373     },
41374
41375     /**
41376      * Toggles the editor between standard and source edit mode.
41377      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41378      */
41379     toggleSourceEdit : function(sourceEditMode){
41380         
41381         this.sourceEditMode = sourceEditMode === true;
41382         
41383         if(this.sourceEditMode){
41384  
41385             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41386             
41387         }else{
41388             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41389             //this.iframe.className = '';
41390             this.deferFocus();
41391         }
41392         //this.setSize(this.owner.wrap.getSize());
41393         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41394     },
41395
41396     
41397   
41398
41399     /**
41400      * Protected method that will not generally be called directly. If you need/want
41401      * custom HTML cleanup, this is the method you should override.
41402      * @param {String} html The HTML to be cleaned
41403      * return {String} The cleaned HTML
41404      */
41405     cleanHtml : function(html){
41406         html = String(html);
41407         if(html.length > 5){
41408             if(Roo.isSafari){ // strip safari nonsense
41409                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41410             }
41411         }
41412         if(html == '&nbsp;'){
41413             html = '';
41414         }
41415         return html;
41416     },
41417
41418     /**
41419      * HTML Editor -> Textarea
41420      * Protected method that will not generally be called directly. Syncs the contents
41421      * of the editor iframe with the textarea.
41422      */
41423     syncValue : function(){
41424         if(this.initialized){
41425             var bd = (this.doc.body || this.doc.documentElement);
41426             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41427             var html = bd.innerHTML;
41428             if(Roo.isSafari){
41429                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41430                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41431                 if(m && m[1]){
41432                     html = '<div style="'+m[0]+'">' + html + '</div>';
41433                 }
41434             }
41435             html = this.cleanHtml(html);
41436             // fix up the special chars.. normaly like back quotes in word...
41437             // however we do not want to do this with chinese..
41438             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41439                 var cc = b.charCodeAt();
41440                 if (
41441                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41442                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41443                     (cc >= 0xf900 && cc < 0xfb00 )
41444                 ) {
41445                         return b;
41446                 }
41447                 return "&#"+cc+";" 
41448             });
41449             if(this.owner.fireEvent('beforesync', this, html) !== false){
41450                 this.el.dom.value = html;
41451                 this.owner.fireEvent('sync', this, html);
41452             }
41453         }
41454     },
41455
41456     /**
41457      * Protected method that will not generally be called directly. Pushes the value of the textarea
41458      * into the iframe editor.
41459      */
41460     pushValue : function(){
41461         if(this.initialized){
41462             var v = this.el.dom.value.trim();
41463             
41464 //            if(v.length < 1){
41465 //                v = '&#160;';
41466 //            }
41467             
41468             if(this.owner.fireEvent('beforepush', this, v) !== false){
41469                 var d = (this.doc.body || this.doc.documentElement);
41470                 d.innerHTML = v;
41471                 this.cleanUpPaste();
41472                 this.el.dom.value = d.innerHTML;
41473                 this.owner.fireEvent('push', this, v);
41474             }
41475         }
41476     },
41477
41478     // private
41479     deferFocus : function(){
41480         this.focus.defer(10, this);
41481     },
41482
41483     // doc'ed in Field
41484     focus : function(){
41485         if(this.win && !this.sourceEditMode){
41486             this.win.focus();
41487         }else{
41488             this.el.focus();
41489         }
41490     },
41491     
41492     assignDocWin: function()
41493     {
41494         var iframe = this.iframe;
41495         
41496          if(Roo.isIE){
41497             this.doc = iframe.contentWindow.document;
41498             this.win = iframe.contentWindow;
41499         } else {
41500 //            if (!Roo.get(this.frameId)) {
41501 //                return;
41502 //            }
41503 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41504 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41505             
41506             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41507                 return;
41508             }
41509             
41510             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41511             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41512         }
41513     },
41514     
41515     // private
41516     initEditor : function(){
41517         //console.log("INIT EDITOR");
41518         this.assignDocWin();
41519         
41520         
41521         
41522         this.doc.designMode="on";
41523         this.doc.open();
41524         this.doc.write(this.getDocMarkup());
41525         this.doc.close();
41526         
41527         var dbody = (this.doc.body || this.doc.documentElement);
41528         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41529         // this copies styles from the containing element into thsi one..
41530         // not sure why we need all of this..
41531         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41532         
41533         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41534         //ss['background-attachment'] = 'fixed'; // w3c
41535         dbody.bgProperties = 'fixed'; // ie
41536         //Roo.DomHelper.applyStyles(dbody, ss);
41537         Roo.EventManager.on(this.doc, {
41538             //'mousedown': this.onEditorEvent,
41539             'mouseup': this.onEditorEvent,
41540             'dblclick': this.onEditorEvent,
41541             'click': this.onEditorEvent,
41542             'keyup': this.onEditorEvent,
41543             buffer:100,
41544             scope: this
41545         });
41546         if(Roo.isGecko){
41547             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41548         }
41549         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41550             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41551         }
41552         this.initialized = true;
41553
41554         this.owner.fireEvent('initialize', this);
41555         this.pushValue();
41556     },
41557
41558     // private
41559     onDestroy : function(){
41560         
41561         
41562         
41563         if(this.rendered){
41564             
41565             //for (var i =0; i < this.toolbars.length;i++) {
41566             //    // fixme - ask toolbars for heights?
41567             //    this.toolbars[i].onDestroy();
41568            // }
41569             
41570             //this.wrap.dom.innerHTML = '';
41571             //this.wrap.remove();
41572         }
41573     },
41574
41575     // private
41576     onFirstFocus : function(){
41577         
41578         this.assignDocWin();
41579         
41580         
41581         this.activated = true;
41582          
41583     
41584         if(Roo.isGecko){ // prevent silly gecko errors
41585             this.win.focus();
41586             var s = this.win.getSelection();
41587             if(!s.focusNode || s.focusNode.nodeType != 3){
41588                 var r = s.getRangeAt(0);
41589                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41590                 r.collapse(true);
41591                 this.deferFocus();
41592             }
41593             try{
41594                 this.execCmd('useCSS', true);
41595                 this.execCmd('styleWithCSS', false);
41596             }catch(e){}
41597         }
41598         this.owner.fireEvent('activate', this);
41599     },
41600
41601     // private
41602     adjustFont: function(btn){
41603         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41604         //if(Roo.isSafari){ // safari
41605         //    adjust *= 2;
41606        // }
41607         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41608         if(Roo.isSafari){ // safari
41609             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41610             v =  (v < 10) ? 10 : v;
41611             v =  (v > 48) ? 48 : v;
41612             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41613             
41614         }
41615         
41616         
41617         v = Math.max(1, v+adjust);
41618         
41619         this.execCmd('FontSize', v  );
41620     },
41621
41622     onEditorEvent : function(e)
41623     {
41624         this.owner.fireEvent('editorevent', this, e);
41625       //  this.updateToolbar();
41626         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41627     },
41628
41629     insertTag : function(tg)
41630     {
41631         // could be a bit smarter... -> wrap the current selected tRoo..
41632         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41633             
41634             range = this.createRange(this.getSelection());
41635             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41636             wrappingNode.appendChild(range.extractContents());
41637             range.insertNode(wrappingNode);
41638
41639             return;
41640             
41641             
41642             
41643         }
41644         this.execCmd("formatblock",   tg);
41645         
41646     },
41647     
41648     insertText : function(txt)
41649     {
41650         
41651         
41652         var range = this.createRange();
41653         range.deleteContents();
41654                //alert(Sender.getAttribute('label'));
41655                
41656         range.insertNode(this.doc.createTextNode(txt));
41657     } ,
41658     
41659      
41660
41661     /**
41662      * Executes a Midas editor command on the editor document and performs necessary focus and
41663      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41664      * @param {String} cmd The Midas command
41665      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41666      */
41667     relayCmd : function(cmd, value){
41668         this.win.focus();
41669         this.execCmd(cmd, value);
41670         this.owner.fireEvent('editorevent', this);
41671         //this.updateToolbar();
41672         this.owner.deferFocus();
41673     },
41674
41675     /**
41676      * Executes a Midas editor command directly on the editor document.
41677      * For visual commands, you should use {@link #relayCmd} instead.
41678      * <b>This should only be called after the editor is initialized.</b>
41679      * @param {String} cmd The Midas command
41680      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41681      */
41682     execCmd : function(cmd, value){
41683         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41684         this.syncValue();
41685     },
41686  
41687  
41688    
41689     /**
41690      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41691      * to insert tRoo.
41692      * @param {String} text | dom node.. 
41693      */
41694     insertAtCursor : function(text)
41695     {
41696         
41697         
41698         
41699         if(!this.activated){
41700             return;
41701         }
41702         /*
41703         if(Roo.isIE){
41704             this.win.focus();
41705             var r = this.doc.selection.createRange();
41706             if(r){
41707                 r.collapse(true);
41708                 r.pasteHTML(text);
41709                 this.syncValue();
41710                 this.deferFocus();
41711             
41712             }
41713             return;
41714         }
41715         */
41716         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41717             this.win.focus();
41718             
41719             
41720             // from jquery ui (MIT licenced)
41721             var range, node;
41722             var win = this.win;
41723             
41724             if (win.getSelection && win.getSelection().getRangeAt) {
41725                 range = win.getSelection().getRangeAt(0);
41726                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41727                 range.insertNode(node);
41728             } else if (win.document.selection && win.document.selection.createRange) {
41729                 // no firefox support
41730                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41731                 win.document.selection.createRange().pasteHTML(txt);
41732             } else {
41733                 // no firefox support
41734                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41735                 this.execCmd('InsertHTML', txt);
41736             } 
41737             
41738             this.syncValue();
41739             
41740             this.deferFocus();
41741         }
41742     },
41743  // private
41744     mozKeyPress : function(e){
41745         if(e.ctrlKey){
41746             var c = e.getCharCode(), cmd;
41747           
41748             if(c > 0){
41749                 c = String.fromCharCode(c).toLowerCase();
41750                 switch(c){
41751                     case 'b':
41752                         cmd = 'bold';
41753                         break;
41754                     case 'i':
41755                         cmd = 'italic';
41756                         break;
41757                     
41758                     case 'u':
41759                         cmd = 'underline';
41760                         break;
41761                     
41762                     case 'v':
41763                         this.cleanUpPaste.defer(100, this);
41764                         return;
41765                         
41766                 }
41767                 if(cmd){
41768                     this.win.focus();
41769                     this.execCmd(cmd);
41770                     this.deferFocus();
41771                     e.preventDefault();
41772                 }
41773                 
41774             }
41775         }
41776     },
41777
41778     // private
41779     fixKeys : function(){ // load time branching for fastest keydown performance
41780         if(Roo.isIE){
41781             return function(e){
41782                 var k = e.getKey(), r;
41783                 if(k == e.TAB){
41784                     e.stopEvent();
41785                     r = this.doc.selection.createRange();
41786                     if(r){
41787                         r.collapse(true);
41788                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41789                         this.deferFocus();
41790                     }
41791                     return;
41792                 }
41793                 
41794                 if(k == e.ENTER){
41795                     r = this.doc.selection.createRange();
41796                     if(r){
41797                         var target = r.parentElement();
41798                         if(!target || target.tagName.toLowerCase() != 'li'){
41799                             e.stopEvent();
41800                             r.pasteHTML('<br />');
41801                             r.collapse(false);
41802                             r.select();
41803                         }
41804                     }
41805                 }
41806                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41807                     this.cleanUpPaste.defer(100, this);
41808                     return;
41809                 }
41810                 
41811                 
41812             };
41813         }else if(Roo.isOpera){
41814             return function(e){
41815                 var k = e.getKey();
41816                 if(k == e.TAB){
41817                     e.stopEvent();
41818                     this.win.focus();
41819                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41820                     this.deferFocus();
41821                 }
41822                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41823                     this.cleanUpPaste.defer(100, this);
41824                     return;
41825                 }
41826                 
41827             };
41828         }else if(Roo.isSafari){
41829             return function(e){
41830                 var k = e.getKey();
41831                 
41832                 if(k == e.TAB){
41833                     e.stopEvent();
41834                     this.execCmd('InsertText','\t');
41835                     this.deferFocus();
41836                     return;
41837                 }
41838                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41839                     this.cleanUpPaste.defer(100, this);
41840                     return;
41841                 }
41842                 
41843              };
41844         }
41845     }(),
41846     
41847     getAllAncestors: function()
41848     {
41849         var p = this.getSelectedNode();
41850         var a = [];
41851         if (!p) {
41852             a.push(p); // push blank onto stack..
41853             p = this.getParentElement();
41854         }
41855         
41856         
41857         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41858             a.push(p);
41859             p = p.parentNode;
41860         }
41861         a.push(this.doc.body);
41862         return a;
41863     },
41864     lastSel : false,
41865     lastSelNode : false,
41866     
41867     
41868     getSelection : function() 
41869     {
41870         this.assignDocWin();
41871         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41872     },
41873     
41874     getSelectedNode: function() 
41875     {
41876         // this may only work on Gecko!!!
41877         
41878         // should we cache this!!!!
41879         
41880         
41881         
41882          
41883         var range = this.createRange(this.getSelection()).cloneRange();
41884         
41885         if (Roo.isIE) {
41886             var parent = range.parentElement();
41887             while (true) {
41888                 var testRange = range.duplicate();
41889                 testRange.moveToElementText(parent);
41890                 if (testRange.inRange(range)) {
41891                     break;
41892                 }
41893                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41894                     break;
41895                 }
41896                 parent = parent.parentElement;
41897             }
41898             return parent;
41899         }
41900         
41901         // is ancestor a text element.
41902         var ac =  range.commonAncestorContainer;
41903         if (ac.nodeType == 3) {
41904             ac = ac.parentNode;
41905         }
41906         
41907         var ar = ac.childNodes;
41908          
41909         var nodes = [];
41910         var other_nodes = [];
41911         var has_other_nodes = false;
41912         for (var i=0;i<ar.length;i++) {
41913             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41914                 continue;
41915             }
41916             // fullly contained node.
41917             
41918             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41919                 nodes.push(ar[i]);
41920                 continue;
41921             }
41922             
41923             // probably selected..
41924             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41925                 other_nodes.push(ar[i]);
41926                 continue;
41927             }
41928             // outer..
41929             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41930                 continue;
41931             }
41932             
41933             
41934             has_other_nodes = true;
41935         }
41936         if (!nodes.length && other_nodes.length) {
41937             nodes= other_nodes;
41938         }
41939         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41940             return false;
41941         }
41942         
41943         return nodes[0];
41944     },
41945     createRange: function(sel)
41946     {
41947         // this has strange effects when using with 
41948         // top toolbar - not sure if it's a great idea.
41949         //this.editor.contentWindow.focus();
41950         if (typeof sel != "undefined") {
41951             try {
41952                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41953             } catch(e) {
41954                 return this.doc.createRange();
41955             }
41956         } else {
41957             return this.doc.createRange();
41958         }
41959     },
41960     getParentElement: function()
41961     {
41962         
41963         this.assignDocWin();
41964         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41965         
41966         var range = this.createRange(sel);
41967          
41968         try {
41969             var p = range.commonAncestorContainer;
41970             while (p.nodeType == 3) { // text node
41971                 p = p.parentNode;
41972             }
41973             return p;
41974         } catch (e) {
41975             return null;
41976         }
41977     
41978     },
41979     /***
41980      *
41981      * Range intersection.. the hard stuff...
41982      *  '-1' = before
41983      *  '0' = hits..
41984      *  '1' = after.
41985      *         [ -- selected range --- ]
41986      *   [fail]                        [fail]
41987      *
41988      *    basically..
41989      *      if end is before start or  hits it. fail.
41990      *      if start is after end or hits it fail.
41991      *
41992      *   if either hits (but other is outside. - then it's not 
41993      *   
41994      *    
41995      **/
41996     
41997     
41998     // @see http://www.thismuchiknow.co.uk/?p=64.
41999     rangeIntersectsNode : function(range, node)
42000     {
42001         var nodeRange = node.ownerDocument.createRange();
42002         try {
42003             nodeRange.selectNode(node);
42004         } catch (e) {
42005             nodeRange.selectNodeContents(node);
42006         }
42007     
42008         var rangeStartRange = range.cloneRange();
42009         rangeStartRange.collapse(true);
42010     
42011         var rangeEndRange = range.cloneRange();
42012         rangeEndRange.collapse(false);
42013     
42014         var nodeStartRange = nodeRange.cloneRange();
42015         nodeStartRange.collapse(true);
42016     
42017         var nodeEndRange = nodeRange.cloneRange();
42018         nodeEndRange.collapse(false);
42019     
42020         return rangeStartRange.compareBoundaryPoints(
42021                  Range.START_TO_START, nodeEndRange) == -1 &&
42022                rangeEndRange.compareBoundaryPoints(
42023                  Range.START_TO_START, nodeStartRange) == 1;
42024         
42025          
42026     },
42027     rangeCompareNode : function(range, node)
42028     {
42029         var nodeRange = node.ownerDocument.createRange();
42030         try {
42031             nodeRange.selectNode(node);
42032         } catch (e) {
42033             nodeRange.selectNodeContents(node);
42034         }
42035         
42036         
42037         range.collapse(true);
42038     
42039         nodeRange.collapse(true);
42040      
42041         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42042         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42043          
42044         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42045         
42046         var nodeIsBefore   =  ss == 1;
42047         var nodeIsAfter    = ee == -1;
42048         
42049         if (nodeIsBefore && nodeIsAfter)
42050             return 0; // outer
42051         if (!nodeIsBefore && nodeIsAfter)
42052             return 1; //right trailed.
42053         
42054         if (nodeIsBefore && !nodeIsAfter)
42055             return 2;  // left trailed.
42056         // fully contined.
42057         return 3;
42058     },
42059
42060     // private? - in a new class?
42061     cleanUpPaste :  function()
42062     {
42063         // cleans up the whole document..
42064         Roo.log('cleanuppaste');
42065         
42066         this.cleanUpChildren(this.doc.body);
42067         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42068         if (clean != this.doc.body.innerHTML) {
42069             this.doc.body.innerHTML = clean;
42070         }
42071         
42072     },
42073     
42074     cleanWordChars : function(input) {// change the chars to hex code
42075         var he = Roo.HtmlEditorCore;
42076         
42077         var output = input;
42078         Roo.each(he.swapCodes, function(sw) { 
42079             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42080             
42081             output = output.replace(swapper, sw[1]);
42082         });
42083         
42084         return output;
42085     },
42086     
42087     
42088     cleanUpChildren : function (n)
42089     {
42090         if (!n.childNodes.length) {
42091             return;
42092         }
42093         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42094            this.cleanUpChild(n.childNodes[i]);
42095         }
42096     },
42097     
42098     
42099         
42100     
42101     cleanUpChild : function (node)
42102     {
42103         var ed = this;
42104         //console.log(node);
42105         if (node.nodeName == "#text") {
42106             // clean up silly Windows -- stuff?
42107             return; 
42108         }
42109         if (node.nodeName == "#comment") {
42110             node.parentNode.removeChild(node);
42111             // clean up silly Windows -- stuff?
42112             return; 
42113         }
42114         var lcname = node.tagName.toLowerCase();
42115         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42116         // whitelist of tags..
42117         
42118         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42119             // remove node.
42120             node.parentNode.removeChild(node);
42121             return;
42122             
42123         }
42124         
42125         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42126         
42127         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42128         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42129         
42130         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42131         //    remove_keep_children = true;
42132         //}
42133         
42134         if (remove_keep_children) {
42135             this.cleanUpChildren(node);
42136             // inserts everything just before this node...
42137             while (node.childNodes.length) {
42138                 var cn = node.childNodes[0];
42139                 node.removeChild(cn);
42140                 node.parentNode.insertBefore(cn, node);
42141             }
42142             node.parentNode.removeChild(node);
42143             return;
42144         }
42145         
42146         if (!node.attributes || !node.attributes.length) {
42147             this.cleanUpChildren(node);
42148             return;
42149         }
42150         
42151         function cleanAttr(n,v)
42152         {
42153             
42154             if (v.match(/^\./) || v.match(/^\//)) {
42155                 return;
42156             }
42157             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42158                 return;
42159             }
42160             if (v.match(/^#/)) {
42161                 return;
42162             }
42163 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42164             node.removeAttribute(n);
42165             
42166         }
42167         
42168         var cwhite = this.cwhite;
42169         var cblack = this.cblack;
42170             
42171         function cleanStyle(n,v)
42172         {
42173             if (v.match(/expression/)) { //XSS?? should we even bother..
42174                 node.removeAttribute(n);
42175                 return;
42176             }
42177             
42178             var parts = v.split(/;/);
42179             var clean = [];
42180             
42181             Roo.each(parts, function(p) {
42182                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42183                 if (!p.length) {
42184                     return true;
42185                 }
42186                 var l = p.split(':').shift().replace(/\s+/g,'');
42187                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42188                 
42189                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42190 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42191                     //node.removeAttribute(n);
42192                     return true;
42193                 }
42194                 //Roo.log()
42195                 // only allow 'c whitelisted system attributes'
42196                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42197 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42198                     //node.removeAttribute(n);
42199                     return true;
42200                 }
42201                 
42202                 
42203                  
42204                 
42205                 clean.push(p);
42206                 return true;
42207             });
42208             if (clean.length) { 
42209                 node.setAttribute(n, clean.join(';'));
42210             } else {
42211                 node.removeAttribute(n);
42212             }
42213             
42214         }
42215         
42216         
42217         for (var i = node.attributes.length-1; i > -1 ; i--) {
42218             var a = node.attributes[i];
42219             //console.log(a);
42220             
42221             if (a.name.toLowerCase().substr(0,2)=='on')  {
42222                 node.removeAttribute(a.name);
42223                 continue;
42224             }
42225             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42226                 node.removeAttribute(a.name);
42227                 continue;
42228             }
42229             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42230                 cleanAttr(a.name,a.value); // fixme..
42231                 continue;
42232             }
42233             if (a.name == 'style') {
42234                 cleanStyle(a.name,a.value);
42235                 continue;
42236             }
42237             /// clean up MS crap..
42238             // tecnically this should be a list of valid class'es..
42239             
42240             
42241             if (a.name == 'class') {
42242                 if (a.value.match(/^Mso/)) {
42243                     node.className = '';
42244                 }
42245                 
42246                 if (a.value.match(/body/)) {
42247                     node.className = '';
42248                 }
42249                 continue;
42250             }
42251             
42252             // style cleanup!?
42253             // class cleanup?
42254             
42255         }
42256         
42257         
42258         this.cleanUpChildren(node);
42259         
42260         
42261     },
42262     
42263     /**
42264      * Clean up MS wordisms...
42265      */
42266     cleanWord : function(node)
42267     {
42268         
42269         
42270         if (!node) {
42271             this.cleanWord(this.doc.body);
42272             return;
42273         }
42274         if (node.nodeName == "#text") {
42275             // clean up silly Windows -- stuff?
42276             return; 
42277         }
42278         if (node.nodeName == "#comment") {
42279             node.parentNode.removeChild(node);
42280             // clean up silly Windows -- stuff?
42281             return; 
42282         }
42283         
42284         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42285             node.parentNode.removeChild(node);
42286             return;
42287         }
42288         
42289         // remove - but keep children..
42290         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42291             while (node.childNodes.length) {
42292                 var cn = node.childNodes[0];
42293                 node.removeChild(cn);
42294                 node.parentNode.insertBefore(cn, node);
42295             }
42296             node.parentNode.removeChild(node);
42297             this.iterateChildren(node, this.cleanWord);
42298             return;
42299         }
42300         // clean styles
42301         if (node.className.length) {
42302             
42303             var cn = node.className.split(/\W+/);
42304             var cna = [];
42305             Roo.each(cn, function(cls) {
42306                 if (cls.match(/Mso[a-zA-Z]+/)) {
42307                     return;
42308                 }
42309                 cna.push(cls);
42310             });
42311             node.className = cna.length ? cna.join(' ') : '';
42312             if (!cna.length) {
42313                 node.removeAttribute("class");
42314             }
42315         }
42316         
42317         if (node.hasAttribute("lang")) {
42318             node.removeAttribute("lang");
42319         }
42320         
42321         if (node.hasAttribute("style")) {
42322             
42323             var styles = node.getAttribute("style").split(";");
42324             var nstyle = [];
42325             Roo.each(styles, function(s) {
42326                 if (!s.match(/:/)) {
42327                     return;
42328                 }
42329                 var kv = s.split(":");
42330                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42331                     return;
42332                 }
42333                 // what ever is left... we allow.
42334                 nstyle.push(s);
42335             });
42336             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42337             if (!nstyle.length) {
42338                 node.removeAttribute('style');
42339             }
42340         }
42341         this.iterateChildren(node, this.cleanWord);
42342         
42343         
42344         
42345     },
42346     /**
42347      * iterateChildren of a Node, calling fn each time, using this as the scole..
42348      * @param {DomNode} node node to iterate children of.
42349      * @param {Function} fn method of this class to call on each item.
42350      */
42351     iterateChildren : function(node, fn)
42352     {
42353         if (!node.childNodes.length) {
42354                 return;
42355         }
42356         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42357            fn.call(this, node.childNodes[i])
42358         }
42359     },
42360     
42361     
42362     /**
42363      * cleanTableWidths.
42364      *
42365      * Quite often pasting from word etc.. results in tables with column and widths.
42366      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42367      *
42368      */
42369     cleanTableWidths : function(node)
42370     {
42371          
42372          
42373         if (!node) {
42374             this.cleanTableWidths(this.doc.body);
42375             return;
42376         }
42377         
42378         // ignore list...
42379         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42380             return; 
42381         }
42382         Roo.log(node.tagName);
42383         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42384             this.iterateChildren(node, this.cleanTableWidths);
42385             return;
42386         }
42387         if (node.hasAttribute('width')) {
42388             node.removeAttribute('width');
42389         }
42390         
42391          
42392         if (node.hasAttribute("style")) {
42393             // pretty basic...
42394             
42395             var styles = node.getAttribute("style").split(";");
42396             var nstyle = [];
42397             Roo.each(styles, function(s) {
42398                 if (!s.match(/:/)) {
42399                     return;
42400                 }
42401                 var kv = s.split(":");
42402                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42403                     return;
42404                 }
42405                 // what ever is left... we allow.
42406                 nstyle.push(s);
42407             });
42408             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42409             if (!nstyle.length) {
42410                 node.removeAttribute('style');
42411             }
42412         }
42413         
42414         this.iterateChildren(node, this.cleanTableWidths);
42415         
42416         
42417     },
42418     
42419     
42420     
42421     
42422     domToHTML : function(currentElement, depth, nopadtext) {
42423         
42424         depth = depth || 0;
42425         nopadtext = nopadtext || false;
42426     
42427         if (!currentElement) {
42428             return this.domToHTML(this.doc.body);
42429         }
42430         
42431         //Roo.log(currentElement);
42432         var j;
42433         var allText = false;
42434         var nodeName = currentElement.nodeName;
42435         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42436         
42437         if  (nodeName == '#text') {
42438             
42439             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42440         }
42441         
42442         
42443         var ret = '';
42444         if (nodeName != 'BODY') {
42445              
42446             var i = 0;
42447             // Prints the node tagName, such as <A>, <IMG>, etc
42448             if (tagName) {
42449                 var attr = [];
42450                 for(i = 0; i < currentElement.attributes.length;i++) {
42451                     // quoting?
42452                     var aname = currentElement.attributes.item(i).name;
42453                     if (!currentElement.attributes.item(i).value.length) {
42454                         continue;
42455                     }
42456                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42457                 }
42458                 
42459                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42460             } 
42461             else {
42462                 
42463                 // eack
42464             }
42465         } else {
42466             tagName = false;
42467         }
42468         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42469             return ret;
42470         }
42471         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42472             nopadtext = true;
42473         }
42474         
42475         
42476         // Traverse the tree
42477         i = 0;
42478         var currentElementChild = currentElement.childNodes.item(i);
42479         var allText = true;
42480         var innerHTML  = '';
42481         lastnode = '';
42482         while (currentElementChild) {
42483             // Formatting code (indent the tree so it looks nice on the screen)
42484             var nopad = nopadtext;
42485             if (lastnode == 'SPAN') {
42486                 nopad  = true;
42487             }
42488             // text
42489             if  (currentElementChild.nodeName == '#text') {
42490                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42491                 toadd = nopadtext ? toadd : toadd.trim();
42492                 if (!nopad && toadd.length > 80) {
42493                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42494                 }
42495                 innerHTML  += toadd;
42496                 
42497                 i++;
42498                 currentElementChild = currentElement.childNodes.item(i);
42499                 lastNode = '';
42500                 continue;
42501             }
42502             allText = false;
42503             
42504             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42505                 
42506             // Recursively traverse the tree structure of the child node
42507             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42508             lastnode = currentElementChild.nodeName;
42509             i++;
42510             currentElementChild=currentElement.childNodes.item(i);
42511         }
42512         
42513         ret += innerHTML;
42514         
42515         if (!allText) {
42516                 // The remaining code is mostly for formatting the tree
42517             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42518         }
42519         
42520         
42521         if (tagName) {
42522             ret+= "</"+tagName+">";
42523         }
42524         return ret;
42525         
42526     },
42527         
42528     applyBlacklists : function()
42529     {
42530         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42531         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42532         
42533         this.white = [];
42534         this.black = [];
42535         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42536             if (b.indexOf(tag) > -1) {
42537                 return;
42538             }
42539             this.white.push(tag);
42540             
42541         }, this);
42542         
42543         Roo.each(w, function(tag) {
42544             if (b.indexOf(tag) > -1) {
42545                 return;
42546             }
42547             if (this.white.indexOf(tag) > -1) {
42548                 return;
42549             }
42550             this.white.push(tag);
42551             
42552         }, this);
42553         
42554         
42555         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42556             if (w.indexOf(tag) > -1) {
42557                 return;
42558             }
42559             this.black.push(tag);
42560             
42561         }, this);
42562         
42563         Roo.each(b, function(tag) {
42564             if (w.indexOf(tag) > -1) {
42565                 return;
42566             }
42567             if (this.black.indexOf(tag) > -1) {
42568                 return;
42569             }
42570             this.black.push(tag);
42571             
42572         }, this);
42573         
42574         
42575         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42576         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42577         
42578         this.cwhite = [];
42579         this.cblack = [];
42580         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42581             if (b.indexOf(tag) > -1) {
42582                 return;
42583             }
42584             this.cwhite.push(tag);
42585             
42586         }, this);
42587         
42588         Roo.each(w, function(tag) {
42589             if (b.indexOf(tag) > -1) {
42590                 return;
42591             }
42592             if (this.cwhite.indexOf(tag) > -1) {
42593                 return;
42594             }
42595             this.cwhite.push(tag);
42596             
42597         }, this);
42598         
42599         
42600         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42601             if (w.indexOf(tag) > -1) {
42602                 return;
42603             }
42604             this.cblack.push(tag);
42605             
42606         }, this);
42607         
42608         Roo.each(b, function(tag) {
42609             if (w.indexOf(tag) > -1) {
42610                 return;
42611             }
42612             if (this.cblack.indexOf(tag) > -1) {
42613                 return;
42614             }
42615             this.cblack.push(tag);
42616             
42617         }, this);
42618     },
42619     
42620     setStylesheets : function(stylesheets)
42621     {
42622         if(typeof(stylesheets) == 'string'){
42623             Roo.get(this.iframe.contentDocument.head).createChild({
42624                 tag : 'link',
42625                 rel : 'stylesheet',
42626                 type : 'text/css',
42627                 href : stylesheets
42628             });
42629             
42630             return;
42631         }
42632         var _this = this;
42633      
42634         Roo.each(stylesheets, function(s) {
42635             if(!s.length){
42636                 return;
42637             }
42638             
42639             Roo.get(_this.iframe.contentDocument.head).createChild({
42640                 tag : 'link',
42641                 rel : 'stylesheet',
42642                 type : 'text/css',
42643                 href : s
42644             });
42645         });
42646
42647         
42648     },
42649     
42650     removeStylesheets : function()
42651     {
42652         var _this = this;
42653         
42654         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42655             s.remove();
42656         });
42657     }
42658     
42659     // hide stuff that is not compatible
42660     /**
42661      * @event blur
42662      * @hide
42663      */
42664     /**
42665      * @event change
42666      * @hide
42667      */
42668     /**
42669      * @event focus
42670      * @hide
42671      */
42672     /**
42673      * @event specialkey
42674      * @hide
42675      */
42676     /**
42677      * @cfg {String} fieldClass @hide
42678      */
42679     /**
42680      * @cfg {String} focusClass @hide
42681      */
42682     /**
42683      * @cfg {String} autoCreate @hide
42684      */
42685     /**
42686      * @cfg {String} inputType @hide
42687      */
42688     /**
42689      * @cfg {String} invalidClass @hide
42690      */
42691     /**
42692      * @cfg {String} invalidText @hide
42693      */
42694     /**
42695      * @cfg {String} msgFx @hide
42696      */
42697     /**
42698      * @cfg {String} validateOnBlur @hide
42699      */
42700 });
42701
42702 Roo.HtmlEditorCore.white = [
42703         'area', 'br', 'img', 'input', 'hr', 'wbr',
42704         
42705        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42706        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42707        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42708        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42709        'table',   'ul',         'xmp', 
42710        
42711        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42712       'thead',   'tr', 
42713      
42714       'dir', 'menu', 'ol', 'ul', 'dl',
42715        
42716       'embed',  'object'
42717 ];
42718
42719
42720 Roo.HtmlEditorCore.black = [
42721     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42722         'applet', // 
42723         'base',   'basefont', 'bgsound', 'blink',  'body', 
42724         'frame',  'frameset', 'head',    'html',   'ilayer', 
42725         'iframe', 'layer',  'link',     'meta',    'object',   
42726         'script', 'style' ,'title',  'xml' // clean later..
42727 ];
42728 Roo.HtmlEditorCore.clean = [
42729     'script', 'style', 'title', 'xml'
42730 ];
42731 Roo.HtmlEditorCore.remove = [
42732     'font'
42733 ];
42734 // attributes..
42735
42736 Roo.HtmlEditorCore.ablack = [
42737     'on'
42738 ];
42739     
42740 Roo.HtmlEditorCore.aclean = [ 
42741     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42742 ];
42743
42744 // protocols..
42745 Roo.HtmlEditorCore.pwhite= [
42746         'http',  'https',  'mailto'
42747 ];
42748
42749 // white listed style attributes.
42750 Roo.HtmlEditorCore.cwhite= [
42751       //  'text-align', /// default is to allow most things..
42752       
42753          
42754 //        'font-size'//??
42755 ];
42756
42757 // black listed style attributes.
42758 Roo.HtmlEditorCore.cblack= [
42759       //  'font-size' -- this can be set by the project 
42760 ];
42761
42762
42763 Roo.HtmlEditorCore.swapCodes   =[ 
42764     [    8211, "--" ], 
42765     [    8212, "--" ], 
42766     [    8216,  "'" ],  
42767     [    8217, "'" ],  
42768     [    8220, '"' ],  
42769     [    8221, '"' ],  
42770     [    8226, "*" ],  
42771     [    8230, "..." ]
42772 ]; 
42773
42774     //<script type="text/javascript">
42775
42776 /*
42777  * Ext JS Library 1.1.1
42778  * Copyright(c) 2006-2007, Ext JS, LLC.
42779  * Licence LGPL
42780  * 
42781  */
42782  
42783  
42784 Roo.form.HtmlEditor = function(config){
42785     
42786     
42787     
42788     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42789     
42790     if (!this.toolbars) {
42791         this.toolbars = [];
42792     }
42793     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42794     
42795     
42796 };
42797
42798 /**
42799  * @class Roo.form.HtmlEditor
42800  * @extends Roo.form.Field
42801  * Provides a lightweight HTML Editor component.
42802  *
42803  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42804  * 
42805  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42806  * supported by this editor.</b><br/><br/>
42807  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42808  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42809  */
42810 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42811     /**
42812      * @cfg {Boolean} clearUp
42813      */
42814     clearUp : true,
42815       /**
42816      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42817      */
42818     toolbars : false,
42819    
42820      /**
42821      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42822      *                        Roo.resizable.
42823      */
42824     resizable : false,
42825      /**
42826      * @cfg {Number} height (in pixels)
42827      */   
42828     height: 300,
42829    /**
42830      * @cfg {Number} width (in pixels)
42831      */   
42832     width: 500,
42833     
42834     /**
42835      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42836      * 
42837      */
42838     stylesheets: false,
42839     
42840     
42841      /**
42842      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42843      * 
42844      */
42845     cblack: false,
42846     /**
42847      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42848      * 
42849      */
42850     cwhite: false,
42851     
42852      /**
42853      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42854      * 
42855      */
42856     black: false,
42857     /**
42858      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42859      * 
42860      */
42861     white: false,
42862     
42863     // id of frame..
42864     frameId: false,
42865     
42866     // private properties
42867     validationEvent : false,
42868     deferHeight: true,
42869     initialized : false,
42870     activated : false,
42871     
42872     onFocus : Roo.emptyFn,
42873     iframePad:3,
42874     hideMode:'offsets',
42875     
42876     actionMode : 'container', // defaults to hiding it...
42877     
42878     defaultAutoCreate : { // modified by initCompnoent..
42879         tag: "textarea",
42880         style:"width:500px;height:300px;",
42881         autocomplete: "new-password"
42882     },
42883
42884     // private
42885     initComponent : function(){
42886         this.addEvents({
42887             /**
42888              * @event initialize
42889              * Fires when the editor is fully initialized (including the iframe)
42890              * @param {HtmlEditor} this
42891              */
42892             initialize: true,
42893             /**
42894              * @event activate
42895              * Fires when the editor is first receives the focus. Any insertion must wait
42896              * until after this event.
42897              * @param {HtmlEditor} this
42898              */
42899             activate: true,
42900              /**
42901              * @event beforesync
42902              * Fires before the textarea is updated with content from the editor iframe. Return false
42903              * to cancel the sync.
42904              * @param {HtmlEditor} this
42905              * @param {String} html
42906              */
42907             beforesync: true,
42908              /**
42909              * @event beforepush
42910              * Fires before the iframe editor is updated with content from the textarea. Return false
42911              * to cancel the push.
42912              * @param {HtmlEditor} this
42913              * @param {String} html
42914              */
42915             beforepush: true,
42916              /**
42917              * @event sync
42918              * Fires when the textarea is updated with content from the editor iframe.
42919              * @param {HtmlEditor} this
42920              * @param {String} html
42921              */
42922             sync: true,
42923              /**
42924              * @event push
42925              * Fires when the iframe editor is updated with content from the textarea.
42926              * @param {HtmlEditor} this
42927              * @param {String} html
42928              */
42929             push: true,
42930              /**
42931              * @event editmodechange
42932              * Fires when the editor switches edit modes
42933              * @param {HtmlEditor} this
42934              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42935              */
42936             editmodechange: true,
42937             /**
42938              * @event editorevent
42939              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42940              * @param {HtmlEditor} this
42941              */
42942             editorevent: true,
42943             /**
42944              * @event firstfocus
42945              * Fires when on first focus - needed by toolbars..
42946              * @param {HtmlEditor} this
42947              */
42948             firstfocus: true,
42949             /**
42950              * @event autosave
42951              * Auto save the htmlEditor value as a file into Events
42952              * @param {HtmlEditor} this
42953              */
42954             autosave: true,
42955             /**
42956              * @event savedpreview
42957              * preview the saved version of htmlEditor
42958              * @param {HtmlEditor} this
42959              */
42960             savedpreview: true,
42961             
42962             /**
42963             * @event stylesheetsclick
42964             * Fires when press the Sytlesheets button
42965             * @param {Roo.HtmlEditorCore} this
42966             */
42967             stylesheetsclick: true
42968         });
42969         this.defaultAutoCreate =  {
42970             tag: "textarea",
42971             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42972             autocomplete: "new-password"
42973         };
42974     },
42975
42976     /**
42977      * Protected method that will not generally be called directly. It
42978      * is called when the editor creates its toolbar. Override this method if you need to
42979      * add custom toolbar buttons.
42980      * @param {HtmlEditor} editor
42981      */
42982     createToolbar : function(editor){
42983         Roo.log("create toolbars");
42984         if (!editor.toolbars || !editor.toolbars.length) {
42985             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42986         }
42987         
42988         for (var i =0 ; i < editor.toolbars.length;i++) {
42989             editor.toolbars[i] = Roo.factory(
42990                     typeof(editor.toolbars[i]) == 'string' ?
42991                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42992                 Roo.form.HtmlEditor);
42993             editor.toolbars[i].init(editor);
42994         }
42995          
42996         
42997     },
42998
42999      
43000     // private
43001     onRender : function(ct, position)
43002     {
43003         var _t = this;
43004         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
43005         
43006         this.wrap = this.el.wrap({
43007             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43008         });
43009         
43010         this.editorcore.onRender(ct, position);
43011          
43012         if (this.resizable) {
43013             this.resizeEl = new Roo.Resizable(this.wrap, {
43014                 pinned : true,
43015                 wrap: true,
43016                 dynamic : true,
43017                 minHeight : this.height,
43018                 height: this.height,
43019                 handles : this.resizable,
43020                 width: this.width,
43021                 listeners : {
43022                     resize : function(r, w, h) {
43023                         _t.onResize(w,h); // -something
43024                     }
43025                 }
43026             });
43027             
43028         }
43029         this.createToolbar(this);
43030        
43031         
43032         if(!this.width){
43033             this.setSize(this.wrap.getSize());
43034         }
43035         if (this.resizeEl) {
43036             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43037             // should trigger onReize..
43038         }
43039         
43040         this.keyNav = new Roo.KeyNav(this.el, {
43041             
43042             "tab" : function(e){
43043                 e.preventDefault();
43044                 
43045                 var value = this.getValue();
43046                 
43047                 var start = this.el.dom.selectionStart;
43048                 var end = this.el.dom.selectionEnd;
43049                 
43050                 if(!e.shiftKey){
43051                     
43052                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43053                     this.el.dom.setSelectionRange(end + 1, end + 1);
43054                     return;
43055                 }
43056                 
43057                 var f = value.substring(0, start).split("\t");
43058                 
43059                 if(f.pop().length != 0){
43060                     return;
43061                 }
43062                 
43063                 this.setValue(f.join("\t") + value.substring(end));
43064                 this.el.dom.setSelectionRange(start - 1, start - 1);
43065                 
43066             },
43067             
43068             "home" : function(e){
43069                 e.preventDefault();
43070                 
43071                 var curr = this.el.dom.selectionStart;
43072                 var lines = this.getValue().split("\n");
43073                 
43074                 if(!lines.length){
43075                     return;
43076                 }
43077                 
43078                 if(e.ctrlKey){
43079                     this.el.dom.setSelectionRange(0, 0);
43080                     return;
43081                 }
43082                 
43083                 var pos = 0;
43084                 
43085                 for (var i = 0; i < lines.length;i++) {
43086                     pos += lines[i].length;
43087                     
43088                     if(i != 0){
43089                         pos += 1;
43090                     }
43091                     
43092                     if(pos < curr){
43093                         continue;
43094                     }
43095                     
43096                     pos -= lines[i].length;
43097                     
43098                     break;
43099                 }
43100                 
43101                 if(!e.shiftKey){
43102                     this.el.dom.setSelectionRange(pos, pos);
43103                     return;
43104                 }
43105                 
43106                 this.el.dom.selectionStart = pos;
43107                 this.el.dom.selectionEnd = curr;
43108             },
43109             
43110             "end" : function(e){
43111                 e.preventDefault();
43112                 
43113                 var curr = this.el.dom.selectionStart;
43114                 var lines = this.getValue().split("\n");
43115                 
43116                 if(!lines.length){
43117                     return;
43118                 }
43119                 
43120                 if(e.ctrlKey){
43121                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43122                     return;
43123                 }
43124                 
43125                 var pos = 0;
43126                 
43127                 for (var i = 0; i < lines.length;i++) {
43128                     
43129                     pos += lines[i].length;
43130                     
43131                     if(i != 0){
43132                         pos += 1;
43133                     }
43134                     
43135                     if(pos < curr){
43136                         continue;
43137                     }
43138                     
43139                     break;
43140                 }
43141                 
43142                 if(!e.shiftKey){
43143                     this.el.dom.setSelectionRange(pos, pos);
43144                     return;
43145                 }
43146                 
43147                 this.el.dom.selectionStart = curr;
43148                 this.el.dom.selectionEnd = pos;
43149             },
43150
43151             scope : this,
43152
43153             doRelay : function(foo, bar, hname){
43154                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43155             },
43156
43157             forceKeyDown: true
43158         });
43159         
43160 //        if(this.autosave && this.w){
43161 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43162 //        }
43163     },
43164
43165     // private
43166     onResize : function(w, h)
43167     {
43168         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43169         var ew = false;
43170         var eh = false;
43171         
43172         if(this.el ){
43173             if(typeof w == 'number'){
43174                 var aw = w - this.wrap.getFrameWidth('lr');
43175                 this.el.setWidth(this.adjustWidth('textarea', aw));
43176                 ew = aw;
43177             }
43178             if(typeof h == 'number'){
43179                 var tbh = 0;
43180                 for (var i =0; i < this.toolbars.length;i++) {
43181                     // fixme - ask toolbars for heights?
43182                     tbh += this.toolbars[i].tb.el.getHeight();
43183                     if (this.toolbars[i].footer) {
43184                         tbh += this.toolbars[i].footer.el.getHeight();
43185                     }
43186                 }
43187                 
43188                 
43189                 
43190                 
43191                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43192                 ah -= 5; // knock a few pixes off for look..
43193 //                Roo.log(ah);
43194                 this.el.setHeight(this.adjustWidth('textarea', ah));
43195                 var eh = ah;
43196             }
43197         }
43198         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43199         this.editorcore.onResize(ew,eh);
43200         
43201     },
43202
43203     /**
43204      * Toggles the editor between standard and source edit mode.
43205      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43206      */
43207     toggleSourceEdit : function(sourceEditMode)
43208     {
43209         this.editorcore.toggleSourceEdit(sourceEditMode);
43210         
43211         if(this.editorcore.sourceEditMode){
43212             Roo.log('editor - showing textarea');
43213             
43214 //            Roo.log('in');
43215 //            Roo.log(this.syncValue());
43216             this.editorcore.syncValue();
43217             this.el.removeClass('x-hidden');
43218             this.el.dom.removeAttribute('tabIndex');
43219             this.el.focus();
43220             
43221             for (var i = 0; i < this.toolbars.length; i++) {
43222                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43223                     this.toolbars[i].tb.hide();
43224                     this.toolbars[i].footer.hide();
43225                 }
43226             }
43227             
43228         }else{
43229             Roo.log('editor - hiding textarea');
43230 //            Roo.log('out')
43231 //            Roo.log(this.pushValue()); 
43232             this.editorcore.pushValue();
43233             
43234             this.el.addClass('x-hidden');
43235             this.el.dom.setAttribute('tabIndex', -1);
43236             
43237             for (var i = 0; i < this.toolbars.length; i++) {
43238                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43239                     this.toolbars[i].tb.show();
43240                     this.toolbars[i].footer.show();
43241                 }
43242             }
43243             
43244             //this.deferFocus();
43245         }
43246         
43247         this.setSize(this.wrap.getSize());
43248         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43249         
43250         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43251     },
43252  
43253     // private (for BoxComponent)
43254     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43255
43256     // private (for BoxComponent)
43257     getResizeEl : function(){
43258         return this.wrap;
43259     },
43260
43261     // private (for BoxComponent)
43262     getPositionEl : function(){
43263         return this.wrap;
43264     },
43265
43266     // private
43267     initEvents : function(){
43268         this.originalValue = this.getValue();
43269     },
43270
43271     /**
43272      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43273      * @method
43274      */
43275     markInvalid : Roo.emptyFn,
43276     /**
43277      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43278      * @method
43279      */
43280     clearInvalid : Roo.emptyFn,
43281
43282     setValue : function(v){
43283         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43284         this.editorcore.pushValue();
43285     },
43286
43287      
43288     // private
43289     deferFocus : function(){
43290         this.focus.defer(10, this);
43291     },
43292
43293     // doc'ed in Field
43294     focus : function(){
43295         this.editorcore.focus();
43296         
43297     },
43298       
43299
43300     // private
43301     onDestroy : function(){
43302         
43303         
43304         
43305         if(this.rendered){
43306             
43307             for (var i =0; i < this.toolbars.length;i++) {
43308                 // fixme - ask toolbars for heights?
43309                 this.toolbars[i].onDestroy();
43310             }
43311             
43312             this.wrap.dom.innerHTML = '';
43313             this.wrap.remove();
43314         }
43315     },
43316
43317     // private
43318     onFirstFocus : function(){
43319         //Roo.log("onFirstFocus");
43320         this.editorcore.onFirstFocus();
43321          for (var i =0; i < this.toolbars.length;i++) {
43322             this.toolbars[i].onFirstFocus();
43323         }
43324         
43325     },
43326     
43327     // private
43328     syncValue : function()
43329     {
43330         this.editorcore.syncValue();
43331     },
43332     
43333     pushValue : function()
43334     {
43335         this.editorcore.pushValue();
43336     },
43337     
43338     setStylesheets : function(stylesheets)
43339     {
43340         this.editorcore.setStylesheets(stylesheets);
43341     },
43342     
43343     removeStylesheets : function()
43344     {
43345         this.editorcore.removeStylesheets();
43346     }
43347      
43348     
43349     // hide stuff that is not compatible
43350     /**
43351      * @event blur
43352      * @hide
43353      */
43354     /**
43355      * @event change
43356      * @hide
43357      */
43358     /**
43359      * @event focus
43360      * @hide
43361      */
43362     /**
43363      * @event specialkey
43364      * @hide
43365      */
43366     /**
43367      * @cfg {String} fieldClass @hide
43368      */
43369     /**
43370      * @cfg {String} focusClass @hide
43371      */
43372     /**
43373      * @cfg {String} autoCreate @hide
43374      */
43375     /**
43376      * @cfg {String} inputType @hide
43377      */
43378     /**
43379      * @cfg {String} invalidClass @hide
43380      */
43381     /**
43382      * @cfg {String} invalidText @hide
43383      */
43384     /**
43385      * @cfg {String} msgFx @hide
43386      */
43387     /**
43388      * @cfg {String} validateOnBlur @hide
43389      */
43390 });
43391  
43392     // <script type="text/javascript">
43393 /*
43394  * Based on
43395  * Ext JS Library 1.1.1
43396  * Copyright(c) 2006-2007, Ext JS, LLC.
43397  *  
43398  
43399  */
43400
43401 /**
43402  * @class Roo.form.HtmlEditorToolbar1
43403  * Basic Toolbar
43404  * 
43405  * Usage:
43406  *
43407  new Roo.form.HtmlEditor({
43408     ....
43409     toolbars : [
43410         new Roo.form.HtmlEditorToolbar1({
43411             disable : { fonts: 1 , format: 1, ..., ... , ...],
43412             btns : [ .... ]
43413         })
43414     }
43415      
43416  * 
43417  * @cfg {Object} disable List of elements to disable..
43418  * @cfg {Array} btns List of additional buttons.
43419  * 
43420  * 
43421  * NEEDS Extra CSS? 
43422  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43423  */
43424  
43425 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43426 {
43427     
43428     Roo.apply(this, config);
43429     
43430     // default disabled, based on 'good practice'..
43431     this.disable = this.disable || {};
43432     Roo.applyIf(this.disable, {
43433         fontSize : true,
43434         colors : true,
43435         specialElements : true
43436     });
43437     
43438     
43439     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43440     // dont call parent... till later.
43441 }
43442
43443 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43444     
43445     tb: false,
43446     
43447     rendered: false,
43448     
43449     editor : false,
43450     editorcore : false,
43451     /**
43452      * @cfg {Object} disable  List of toolbar elements to disable
43453          
43454      */
43455     disable : false,
43456     
43457     
43458      /**
43459      * @cfg {String} createLinkText The default text for the create link prompt
43460      */
43461     createLinkText : 'Please enter the URL for the link:',
43462     /**
43463      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43464      */
43465     defaultLinkValue : 'http:/'+'/',
43466    
43467     
43468       /**
43469      * @cfg {Array} fontFamilies An array of available font families
43470      */
43471     fontFamilies : [
43472         'Arial',
43473         'Courier New',
43474         'Tahoma',
43475         'Times New Roman',
43476         'Verdana'
43477     ],
43478     
43479     specialChars : [
43480            "&#169;",
43481           "&#174;",     
43482           "&#8482;",    
43483           "&#163;" ,    
43484          // "&#8212;",    
43485           "&#8230;",    
43486           "&#247;" ,    
43487         //  "&#225;" ,     ?? a acute?
43488            "&#8364;"    , //Euro
43489        //   "&#8220;"    ,
43490         //  "&#8221;"    ,
43491         //  "&#8226;"    ,
43492           "&#176;"  //   , // degrees
43493
43494          // "&#233;"     , // e ecute
43495          // "&#250;"     , // u ecute?
43496     ],
43497     
43498     specialElements : [
43499         {
43500             text: "Insert Table",
43501             xtype: 'MenuItem',
43502             xns : Roo.Menu,
43503             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43504                 
43505         },
43506         {    
43507             text: "Insert Image",
43508             xtype: 'MenuItem',
43509             xns : Roo.Menu,
43510             ihtml : '<img src="about:blank"/>'
43511             
43512         }
43513         
43514          
43515     ],
43516     
43517     
43518     inputElements : [ 
43519             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43520             "input:submit", "input:button", "select", "textarea", "label" ],
43521     formats : [
43522         ["p"] ,  
43523         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43524         ["pre"],[ "code"], 
43525         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43526         ['div'],['span']
43527     ],
43528     
43529     cleanStyles : [
43530         "font-size"
43531     ],
43532      /**
43533      * @cfg {String} defaultFont default font to use.
43534      */
43535     defaultFont: 'tahoma',
43536    
43537     fontSelect : false,
43538     
43539     
43540     formatCombo : false,
43541     
43542     init : function(editor)
43543     {
43544         this.editor = editor;
43545         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43546         var editorcore = this.editorcore;
43547         
43548         var _t = this;
43549         
43550         var fid = editorcore.frameId;
43551         var etb = this;
43552         function btn(id, toggle, handler){
43553             var xid = fid + '-'+ id ;
43554             return {
43555                 id : xid,
43556                 cmd : id,
43557                 cls : 'x-btn-icon x-edit-'+id,
43558                 enableToggle:toggle !== false,
43559                 scope: _t, // was editor...
43560                 handler:handler||_t.relayBtnCmd,
43561                 clickEvent:'mousedown',
43562                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43563                 tabIndex:-1
43564             };
43565         }
43566         
43567         
43568         
43569         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43570         this.tb = tb;
43571          // stop form submits
43572         tb.el.on('click', function(e){
43573             e.preventDefault(); // what does this do?
43574         });
43575
43576         if(!this.disable.font) { // && !Roo.isSafari){
43577             /* why no safari for fonts 
43578             editor.fontSelect = tb.el.createChild({
43579                 tag:'select',
43580                 tabIndex: -1,
43581                 cls:'x-font-select',
43582                 html: this.createFontOptions()
43583             });
43584             
43585             editor.fontSelect.on('change', function(){
43586                 var font = editor.fontSelect.dom.value;
43587                 editor.relayCmd('fontname', font);
43588                 editor.deferFocus();
43589             }, editor);
43590             
43591             tb.add(
43592                 editor.fontSelect.dom,
43593                 '-'
43594             );
43595             */
43596             
43597         };
43598         if(!this.disable.formats){
43599             this.formatCombo = new Roo.form.ComboBox({
43600                 store: new Roo.data.SimpleStore({
43601                     id : 'tag',
43602                     fields: ['tag'],
43603                     data : this.formats // from states.js
43604                 }),
43605                 blockFocus : true,
43606                 name : '',
43607                 //autoCreate : {tag: "div",  size: "20"},
43608                 displayField:'tag',
43609                 typeAhead: false,
43610                 mode: 'local',
43611                 editable : false,
43612                 triggerAction: 'all',
43613                 emptyText:'Add tag',
43614                 selectOnFocus:true,
43615                 width:135,
43616                 listeners : {
43617                     'select': function(c, r, i) {
43618                         editorcore.insertTag(r.get('tag'));
43619                         editor.focus();
43620                     }
43621                 }
43622
43623             });
43624             tb.addField(this.formatCombo);
43625             
43626         }
43627         
43628         if(!this.disable.format){
43629             tb.add(
43630                 btn('bold'),
43631                 btn('italic'),
43632                 btn('underline')
43633             );
43634         };
43635         if(!this.disable.fontSize){
43636             tb.add(
43637                 '-',
43638                 
43639                 
43640                 btn('increasefontsize', false, editorcore.adjustFont),
43641                 btn('decreasefontsize', false, editorcore.adjustFont)
43642             );
43643         };
43644         
43645         
43646         if(!this.disable.colors){
43647             tb.add(
43648                 '-', {
43649                     id:editorcore.frameId +'-forecolor',
43650                     cls:'x-btn-icon x-edit-forecolor',
43651                     clickEvent:'mousedown',
43652                     tooltip: this.buttonTips['forecolor'] || undefined,
43653                     tabIndex:-1,
43654                     menu : new Roo.menu.ColorMenu({
43655                         allowReselect: true,
43656                         focus: Roo.emptyFn,
43657                         value:'000000',
43658                         plain:true,
43659                         selectHandler: function(cp, color){
43660                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43661                             editor.deferFocus();
43662                         },
43663                         scope: editorcore,
43664                         clickEvent:'mousedown'
43665                     })
43666                 }, {
43667                     id:editorcore.frameId +'backcolor',
43668                     cls:'x-btn-icon x-edit-backcolor',
43669                     clickEvent:'mousedown',
43670                     tooltip: this.buttonTips['backcolor'] || undefined,
43671                     tabIndex:-1,
43672                     menu : new Roo.menu.ColorMenu({
43673                         focus: Roo.emptyFn,
43674                         value:'FFFFFF',
43675                         plain:true,
43676                         allowReselect: true,
43677                         selectHandler: function(cp, color){
43678                             if(Roo.isGecko){
43679                                 editorcore.execCmd('useCSS', false);
43680                                 editorcore.execCmd('hilitecolor', color);
43681                                 editorcore.execCmd('useCSS', true);
43682                                 editor.deferFocus();
43683                             }else{
43684                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43685                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43686                                 editor.deferFocus();
43687                             }
43688                         },
43689                         scope:editorcore,
43690                         clickEvent:'mousedown'
43691                     })
43692                 }
43693             );
43694         };
43695         // now add all the items...
43696         
43697
43698         if(!this.disable.alignments){
43699             tb.add(
43700                 '-',
43701                 btn('justifyleft'),
43702                 btn('justifycenter'),
43703                 btn('justifyright')
43704             );
43705         };
43706
43707         //if(!Roo.isSafari){
43708             if(!this.disable.links){
43709                 tb.add(
43710                     '-',
43711                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43712                 );
43713             };
43714
43715             if(!this.disable.lists){
43716                 tb.add(
43717                     '-',
43718                     btn('insertorderedlist'),
43719                     btn('insertunorderedlist')
43720                 );
43721             }
43722             if(!this.disable.sourceEdit){
43723                 tb.add(
43724                     '-',
43725                     btn('sourceedit', true, function(btn){
43726                         this.toggleSourceEdit(btn.pressed);
43727                     })
43728                 );
43729             }
43730         //}
43731         
43732         var smenu = { };
43733         // special menu.. - needs to be tidied up..
43734         if (!this.disable.special) {
43735             smenu = {
43736                 text: "&#169;",
43737                 cls: 'x-edit-none',
43738                 
43739                 menu : {
43740                     items : []
43741                 }
43742             };
43743             for (var i =0; i < this.specialChars.length; i++) {
43744                 smenu.menu.items.push({
43745                     
43746                     html: this.specialChars[i],
43747                     handler: function(a,b) {
43748                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43749                         //editor.insertAtCursor(a.html);
43750                         
43751                     },
43752                     tabIndex:-1
43753                 });
43754             }
43755             
43756             
43757             tb.add(smenu);
43758             
43759             
43760         }
43761         
43762         var cmenu = { };
43763         if (!this.disable.cleanStyles) {
43764             cmenu = {
43765                 cls: 'x-btn-icon x-btn-clear',
43766                 
43767                 menu : {
43768                     items : []
43769                 }
43770             };
43771             for (var i =0; i < this.cleanStyles.length; i++) {
43772                 cmenu.menu.items.push({
43773                     actiontype : this.cleanStyles[i],
43774                     html: 'Remove ' + this.cleanStyles[i],
43775                     handler: function(a,b) {
43776 //                        Roo.log(a);
43777 //                        Roo.log(b);
43778                         var c = Roo.get(editorcore.doc.body);
43779                         c.select('[style]').each(function(s) {
43780                             s.dom.style.removeProperty(a.actiontype);
43781                         });
43782                         editorcore.syncValue();
43783                     },
43784                     tabIndex:-1
43785                 });
43786             }
43787              cmenu.menu.items.push({
43788                 actiontype : 'tablewidths',
43789                 html: 'Remove Table Widths',
43790                 handler: function(a,b) {
43791                     editorcore.cleanTableWidths();
43792                     editorcore.syncValue();
43793                 },
43794                 tabIndex:-1
43795             });
43796             cmenu.menu.items.push({
43797                 actiontype : 'word',
43798                 html: 'Remove MS Word Formating',
43799                 handler: function(a,b) {
43800                     editorcore.cleanWord();
43801                     editorcore.syncValue();
43802                 },
43803                 tabIndex:-1
43804             });
43805             
43806             cmenu.menu.items.push({
43807                 actiontype : 'all',
43808                 html: 'Remove All Styles',
43809                 handler: function(a,b) {
43810                     
43811                     var c = Roo.get(editorcore.doc.body);
43812                     c.select('[style]').each(function(s) {
43813                         s.dom.removeAttribute('style');
43814                     });
43815                     editorcore.syncValue();
43816                 },
43817                 tabIndex:-1
43818             });
43819             
43820             cmenu.menu.items.push({
43821                 actiontype : 'all',
43822                 html: 'Remove All CSS Classes',
43823                 handler: function(a,b) {
43824                     
43825                     var c = Roo.get(editorcore.doc.body);
43826                     c.select('[class]').each(function(s) {
43827                         s.dom.className = '';
43828                     });
43829                     editorcore.syncValue();
43830                 },
43831                 tabIndex:-1
43832             });
43833             
43834              cmenu.menu.items.push({
43835                 actiontype : 'tidy',
43836                 html: 'Tidy HTML Source',
43837                 handler: function(a,b) {
43838                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43839                     editorcore.syncValue();
43840                 },
43841                 tabIndex:-1
43842             });
43843             
43844             
43845             tb.add(cmenu);
43846         }
43847          
43848         if (!this.disable.specialElements) {
43849             var semenu = {
43850                 text: "Other;",
43851                 cls: 'x-edit-none',
43852                 menu : {
43853                     items : []
43854                 }
43855             };
43856             for (var i =0; i < this.specialElements.length; i++) {
43857                 semenu.menu.items.push(
43858                     Roo.apply({ 
43859                         handler: function(a,b) {
43860                             editor.insertAtCursor(this.ihtml);
43861                         }
43862                     }, this.specialElements[i])
43863                 );
43864                     
43865             }
43866             
43867             tb.add(semenu);
43868             
43869             
43870         }
43871          
43872         
43873         if (this.btns) {
43874             for(var i =0; i< this.btns.length;i++) {
43875                 var b = Roo.factory(this.btns[i],Roo.form);
43876                 b.cls =  'x-edit-none';
43877                 
43878                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43879                     b.cls += ' x-init-enable';
43880                 }
43881                 
43882                 b.scope = editorcore;
43883                 tb.add(b);
43884             }
43885         
43886         }
43887         
43888         
43889         
43890         // disable everything...
43891         
43892         this.tb.items.each(function(item){
43893             
43894            if(
43895                 item.id != editorcore.frameId+ '-sourceedit' && 
43896                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43897             ){
43898                 
43899                 item.disable();
43900             }
43901         });
43902         this.rendered = true;
43903         
43904         // the all the btns;
43905         editor.on('editorevent', this.updateToolbar, this);
43906         // other toolbars need to implement this..
43907         //editor.on('editmodechange', this.updateToolbar, this);
43908     },
43909     
43910     
43911     relayBtnCmd : function(btn) {
43912         this.editorcore.relayCmd(btn.cmd);
43913     },
43914     // private used internally
43915     createLink : function(){
43916         Roo.log("create link?");
43917         var url = prompt(this.createLinkText, this.defaultLinkValue);
43918         if(url && url != 'http:/'+'/'){
43919             this.editorcore.relayCmd('createlink', url);
43920         }
43921     },
43922
43923     
43924     /**
43925      * Protected method that will not generally be called directly. It triggers
43926      * a toolbar update by reading the markup state of the current selection in the editor.
43927      */
43928     updateToolbar: function(){
43929
43930         if(!this.editorcore.activated){
43931             this.editor.onFirstFocus();
43932             return;
43933         }
43934
43935         var btns = this.tb.items.map, 
43936             doc = this.editorcore.doc,
43937             frameId = this.editorcore.frameId;
43938
43939         if(!this.disable.font && !Roo.isSafari){
43940             /*
43941             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43942             if(name != this.fontSelect.dom.value){
43943                 this.fontSelect.dom.value = name;
43944             }
43945             */
43946         }
43947         if(!this.disable.format){
43948             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43949             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43950             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43951         }
43952         if(!this.disable.alignments){
43953             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43954             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43955             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43956         }
43957         if(!Roo.isSafari && !this.disable.lists){
43958             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43959             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43960         }
43961         
43962         var ans = this.editorcore.getAllAncestors();
43963         if (this.formatCombo) {
43964             
43965             
43966             var store = this.formatCombo.store;
43967             this.formatCombo.setValue("");
43968             for (var i =0; i < ans.length;i++) {
43969                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43970                     // select it..
43971                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43972                     break;
43973                 }
43974             }
43975         }
43976         
43977         
43978         
43979         // hides menus... - so this cant be on a menu...
43980         Roo.menu.MenuMgr.hideAll();
43981
43982         //this.editorsyncValue();
43983     },
43984    
43985     
43986     createFontOptions : function(){
43987         var buf = [], fs = this.fontFamilies, ff, lc;
43988         
43989         
43990         
43991         for(var i = 0, len = fs.length; i< len; i++){
43992             ff = fs[i];
43993             lc = ff.toLowerCase();
43994             buf.push(
43995                 '<option value="',lc,'" style="font-family:',ff,';"',
43996                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43997                     ff,
43998                 '</option>'
43999             );
44000         }
44001         return buf.join('');
44002     },
44003     
44004     toggleSourceEdit : function(sourceEditMode){
44005         
44006         Roo.log("toolbar toogle");
44007         if(sourceEditMode === undefined){
44008             sourceEditMode = !this.sourceEditMode;
44009         }
44010         this.sourceEditMode = sourceEditMode === true;
44011         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44012         // just toggle the button?
44013         if(btn.pressed !== this.sourceEditMode){
44014             btn.toggle(this.sourceEditMode);
44015             return;
44016         }
44017         
44018         if(sourceEditMode){
44019             Roo.log("disabling buttons");
44020             this.tb.items.each(function(item){
44021                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44022                     item.disable();
44023                 }
44024             });
44025           
44026         }else{
44027             Roo.log("enabling buttons");
44028             if(this.editorcore.initialized){
44029                 this.tb.items.each(function(item){
44030                     item.enable();
44031                 });
44032             }
44033             
44034         }
44035         Roo.log("calling toggole on editor");
44036         // tell the editor that it's been pressed..
44037         this.editor.toggleSourceEdit(sourceEditMode);
44038        
44039     },
44040      /**
44041      * Object collection of toolbar tooltips for the buttons in the editor. The key
44042      * is the command id associated with that button and the value is a valid QuickTips object.
44043      * For example:
44044 <pre><code>
44045 {
44046     bold : {
44047         title: 'Bold (Ctrl+B)',
44048         text: 'Make the selected text bold.',
44049         cls: 'x-html-editor-tip'
44050     },
44051     italic : {
44052         title: 'Italic (Ctrl+I)',
44053         text: 'Make the selected text italic.',
44054         cls: 'x-html-editor-tip'
44055     },
44056     ...
44057 </code></pre>
44058     * @type Object
44059      */
44060     buttonTips : {
44061         bold : {
44062             title: 'Bold (Ctrl+B)',
44063             text: 'Make the selected text bold.',
44064             cls: 'x-html-editor-tip'
44065         },
44066         italic : {
44067             title: 'Italic (Ctrl+I)',
44068             text: 'Make the selected text italic.',
44069             cls: 'x-html-editor-tip'
44070         },
44071         underline : {
44072             title: 'Underline (Ctrl+U)',
44073             text: 'Underline the selected text.',
44074             cls: 'x-html-editor-tip'
44075         },
44076         increasefontsize : {
44077             title: 'Grow Text',
44078             text: 'Increase the font size.',
44079             cls: 'x-html-editor-tip'
44080         },
44081         decreasefontsize : {
44082             title: 'Shrink Text',
44083             text: 'Decrease the font size.',
44084             cls: 'x-html-editor-tip'
44085         },
44086         backcolor : {
44087             title: 'Text Highlight Color',
44088             text: 'Change the background color of the selected text.',
44089             cls: 'x-html-editor-tip'
44090         },
44091         forecolor : {
44092             title: 'Font Color',
44093             text: 'Change the color of the selected text.',
44094             cls: 'x-html-editor-tip'
44095         },
44096         justifyleft : {
44097             title: 'Align Text Left',
44098             text: 'Align text to the left.',
44099             cls: 'x-html-editor-tip'
44100         },
44101         justifycenter : {
44102             title: 'Center Text',
44103             text: 'Center text in the editor.',
44104             cls: 'x-html-editor-tip'
44105         },
44106         justifyright : {
44107             title: 'Align Text Right',
44108             text: 'Align text to the right.',
44109             cls: 'x-html-editor-tip'
44110         },
44111         insertunorderedlist : {
44112             title: 'Bullet List',
44113             text: 'Start a bulleted list.',
44114             cls: 'x-html-editor-tip'
44115         },
44116         insertorderedlist : {
44117             title: 'Numbered List',
44118             text: 'Start a numbered list.',
44119             cls: 'x-html-editor-tip'
44120         },
44121         createlink : {
44122             title: 'Hyperlink',
44123             text: 'Make the selected text a hyperlink.',
44124             cls: 'x-html-editor-tip'
44125         },
44126         sourceedit : {
44127             title: 'Source Edit',
44128             text: 'Switch to source editing mode.',
44129             cls: 'x-html-editor-tip'
44130         }
44131     },
44132     // private
44133     onDestroy : function(){
44134         if(this.rendered){
44135             
44136             this.tb.items.each(function(item){
44137                 if(item.menu){
44138                     item.menu.removeAll();
44139                     if(item.menu.el){
44140                         item.menu.el.destroy();
44141                     }
44142                 }
44143                 item.destroy();
44144             });
44145              
44146         }
44147     },
44148     onFirstFocus: function() {
44149         this.tb.items.each(function(item){
44150            item.enable();
44151         });
44152     }
44153 });
44154
44155
44156
44157
44158 // <script type="text/javascript">
44159 /*
44160  * Based on
44161  * Ext JS Library 1.1.1
44162  * Copyright(c) 2006-2007, Ext JS, LLC.
44163  *  
44164  
44165  */
44166
44167  
44168 /**
44169  * @class Roo.form.HtmlEditor.ToolbarContext
44170  * Context Toolbar
44171  * 
44172  * Usage:
44173  *
44174  new Roo.form.HtmlEditor({
44175     ....
44176     toolbars : [
44177         { xtype: 'ToolbarStandard', styles : {} }
44178         { xtype: 'ToolbarContext', disable : {} }
44179     ]
44180 })
44181
44182      
44183  * 
44184  * @config : {Object} disable List of elements to disable.. (not done yet.)
44185  * @config : {Object} styles  Map of styles available.
44186  * 
44187  */
44188
44189 Roo.form.HtmlEditor.ToolbarContext = function(config)
44190 {
44191     
44192     Roo.apply(this, config);
44193     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44194     // dont call parent... till later.
44195     this.styles = this.styles || {};
44196 }
44197
44198  
44199
44200 Roo.form.HtmlEditor.ToolbarContext.types = {
44201     'IMG' : {
44202         width : {
44203             title: "Width",
44204             width: 40
44205         },
44206         height:  {
44207             title: "Height",
44208             width: 40
44209         },
44210         align: {
44211             title: "Align",
44212             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44213             width : 80
44214             
44215         },
44216         border: {
44217             title: "Border",
44218             width: 40
44219         },
44220         alt: {
44221             title: "Alt",
44222             width: 120
44223         },
44224         src : {
44225             title: "Src",
44226             width: 220
44227         }
44228         
44229     },
44230     'A' : {
44231         name : {
44232             title: "Name",
44233             width: 50
44234         },
44235         target:  {
44236             title: "Target",
44237             width: 120
44238         },
44239         href:  {
44240             title: "Href",
44241             width: 220
44242         } // border?
44243         
44244     },
44245     'TABLE' : {
44246         rows : {
44247             title: "Rows",
44248             width: 20
44249         },
44250         cols : {
44251             title: "Cols",
44252             width: 20
44253         },
44254         width : {
44255             title: "Width",
44256             width: 40
44257         },
44258         height : {
44259             title: "Height",
44260             width: 40
44261         },
44262         border : {
44263             title: "Border",
44264             width: 20
44265         }
44266     },
44267     'TD' : {
44268         width : {
44269             title: "Width",
44270             width: 40
44271         },
44272         height : {
44273             title: "Height",
44274             width: 40
44275         },   
44276         align: {
44277             title: "Align",
44278             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44279             width: 80
44280         },
44281         valign: {
44282             title: "Valign",
44283             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44284             width: 80
44285         },
44286         colspan: {
44287             title: "Colspan",
44288             width: 20
44289             
44290         },
44291          'font-family'  : {
44292             title : "Font",
44293             style : 'fontFamily',
44294             displayField: 'display',
44295             optname : 'font-family',
44296             width: 140
44297         }
44298     },
44299     'INPUT' : {
44300         name : {
44301             title: "name",
44302             width: 120
44303         },
44304         value : {
44305             title: "Value",
44306             width: 120
44307         },
44308         width : {
44309             title: "Width",
44310             width: 40
44311         }
44312     },
44313     'LABEL' : {
44314         'for' : {
44315             title: "For",
44316             width: 120
44317         }
44318     },
44319     'TEXTAREA' : {
44320           name : {
44321             title: "name",
44322             width: 120
44323         },
44324         rows : {
44325             title: "Rows",
44326             width: 20
44327         },
44328         cols : {
44329             title: "Cols",
44330             width: 20
44331         }
44332     },
44333     'SELECT' : {
44334         name : {
44335             title: "name",
44336             width: 120
44337         },
44338         selectoptions : {
44339             title: "Options",
44340             width: 200
44341         }
44342     },
44343     
44344     // should we really allow this??
44345     // should this just be 
44346     'BODY' : {
44347         title : {
44348             title: "Title",
44349             width: 200,
44350             disabled : true
44351         }
44352     },
44353     'SPAN' : {
44354         'font-family'  : {
44355             title : "Font",
44356             style : 'fontFamily',
44357             displayField: 'display',
44358             optname : 'font-family',
44359             width: 140
44360         }
44361     },
44362     'DIV' : {
44363         'font-family'  : {
44364             title : "Font",
44365             style : 'fontFamily',
44366             displayField: 'display',
44367             optname : 'font-family',
44368             width: 140
44369         }
44370     },
44371      'P' : {
44372         'font-family'  : {
44373             title : "Font",
44374             style : 'fontFamily',
44375             displayField: 'display',
44376             optname : 'font-family',
44377             width: 140
44378         }
44379     },
44380     
44381     '*' : {
44382         // empty..
44383     }
44384
44385 };
44386
44387 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44388 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44389
44390 Roo.form.HtmlEditor.ToolbarContext.options = {
44391         'font-family'  : [ 
44392                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44393                 [ 'Courier New', 'Courier New'],
44394                 [ 'Tahoma', 'Tahoma'],
44395                 [ 'Times New Roman,serif', 'Times'],
44396                 [ 'Verdana','Verdana' ]
44397         ]
44398 };
44399
44400 // fixme - these need to be configurable..
44401  
44402
44403 Roo.form.HtmlEditor.ToolbarContext.types
44404
44405
44406 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44407     
44408     tb: false,
44409     
44410     rendered: false,
44411     
44412     editor : false,
44413     editorcore : false,
44414     /**
44415      * @cfg {Object} disable  List of toolbar elements to disable
44416          
44417      */
44418     disable : false,
44419     /**
44420      * @cfg {Object} styles List of styles 
44421      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44422      *
44423      * These must be defined in the page, so they get rendered correctly..
44424      * .headline { }
44425      * TD.underline { }
44426      * 
44427      */
44428     styles : false,
44429     
44430     options: false,
44431     
44432     toolbars : false,
44433     
44434     init : function(editor)
44435     {
44436         this.editor = editor;
44437         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44438         var editorcore = this.editorcore;
44439         
44440         var fid = editorcore.frameId;
44441         var etb = this;
44442         function btn(id, toggle, handler){
44443             var xid = fid + '-'+ id ;
44444             return {
44445                 id : xid,
44446                 cmd : id,
44447                 cls : 'x-btn-icon x-edit-'+id,
44448                 enableToggle:toggle !== false,
44449                 scope: editorcore, // was editor...
44450                 handler:handler||editorcore.relayBtnCmd,
44451                 clickEvent:'mousedown',
44452                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44453                 tabIndex:-1
44454             };
44455         }
44456         // create a new element.
44457         var wdiv = editor.wrap.createChild({
44458                 tag: 'div'
44459             }, editor.wrap.dom.firstChild.nextSibling, true);
44460         
44461         // can we do this more than once??
44462         
44463          // stop form submits
44464       
44465  
44466         // disable everything...
44467         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44468         this.toolbars = {};
44469            
44470         for (var i in  ty) {
44471           
44472             this.toolbars[i] = this.buildToolbar(ty[i],i);
44473         }
44474         this.tb = this.toolbars.BODY;
44475         this.tb.el.show();
44476         this.buildFooter();
44477         this.footer.show();
44478         editor.on('hide', function( ) { this.footer.hide() }, this);
44479         editor.on('show', function( ) { this.footer.show() }, this);
44480         
44481          
44482         this.rendered = true;
44483         
44484         // the all the btns;
44485         editor.on('editorevent', this.updateToolbar, this);
44486         // other toolbars need to implement this..
44487         //editor.on('editmodechange', this.updateToolbar, this);
44488     },
44489     
44490     
44491     
44492     /**
44493      * Protected method that will not generally be called directly. It triggers
44494      * a toolbar update by reading the markup state of the current selection in the editor.
44495      *
44496      * Note you can force an update by calling on('editorevent', scope, false)
44497      */
44498     updateToolbar: function(editor,ev,sel){
44499
44500         //Roo.log(ev);
44501         // capture mouse up - this is handy for selecting images..
44502         // perhaps should go somewhere else...
44503         if(!this.editorcore.activated){
44504              this.editor.onFirstFocus();
44505             return;
44506         }
44507         
44508         
44509         
44510         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44511         // selectNode - might want to handle IE?
44512         if (ev &&
44513             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44514             ev.target && ev.target.tagName == 'IMG') {
44515             // they have click on an image...
44516             // let's see if we can change the selection...
44517             sel = ev.target;
44518          
44519               var nodeRange = sel.ownerDocument.createRange();
44520             try {
44521                 nodeRange.selectNode(sel);
44522             } catch (e) {
44523                 nodeRange.selectNodeContents(sel);
44524             }
44525             //nodeRange.collapse(true);
44526             var s = this.editorcore.win.getSelection();
44527             s.removeAllRanges();
44528             s.addRange(nodeRange);
44529         }  
44530         
44531       
44532         var updateFooter = sel ? false : true;
44533         
44534         
44535         var ans = this.editorcore.getAllAncestors();
44536         
44537         // pick
44538         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44539         
44540         if (!sel) { 
44541             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44542             sel = sel ? sel : this.editorcore.doc.body;
44543             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44544             
44545         }
44546         // pick a menu that exists..
44547         var tn = sel.tagName.toUpperCase();
44548         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44549         
44550         tn = sel.tagName.toUpperCase();
44551         
44552         var lastSel = this.tb.selectedNode
44553         
44554         this.tb.selectedNode = sel;
44555         
44556         // if current menu does not match..
44557         
44558         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44559                 
44560             this.tb.el.hide();
44561             ///console.log("show: " + tn);
44562             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44563             this.tb.el.show();
44564             // update name
44565             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44566             
44567             
44568             // update attributes
44569             if (this.tb.fields) {
44570                 this.tb.fields.each(function(e) {
44571                     if (e.stylename) {
44572                         e.setValue(sel.style[e.stylename]);
44573                         return;
44574                     } 
44575                    e.setValue(sel.getAttribute(e.attrname));
44576                 });
44577             }
44578             
44579             var hasStyles = false;
44580             for(var i in this.styles) {
44581                 hasStyles = true;
44582                 break;
44583             }
44584             
44585             // update styles
44586             if (hasStyles) { 
44587                 var st = this.tb.fields.item(0);
44588                 
44589                 st.store.removeAll();
44590                
44591                 
44592                 var cn = sel.className.split(/\s+/);
44593                 
44594                 var avs = [];
44595                 if (this.styles['*']) {
44596                     
44597                     Roo.each(this.styles['*'], function(v) {
44598                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44599                     });
44600                 }
44601                 if (this.styles[tn]) { 
44602                     Roo.each(this.styles[tn], function(v) {
44603                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44604                     });
44605                 }
44606                 
44607                 st.store.loadData(avs);
44608                 st.collapse();
44609                 st.setValue(cn);
44610             }
44611             // flag our selected Node.
44612             this.tb.selectedNode = sel;
44613            
44614            
44615             Roo.menu.MenuMgr.hideAll();
44616
44617         }
44618         
44619         if (!updateFooter) {
44620             //this.footDisp.dom.innerHTML = ''; 
44621             return;
44622         }
44623         // update the footer
44624         //
44625         var html = '';
44626         
44627         this.footerEls = ans.reverse();
44628         Roo.each(this.footerEls, function(a,i) {
44629             if (!a) { return; }
44630             html += html.length ? ' &gt; '  :  '';
44631             
44632             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44633             
44634         });
44635        
44636         // 
44637         var sz = this.footDisp.up('td').getSize();
44638         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44639         this.footDisp.dom.style.marginLeft = '5px';
44640         
44641         this.footDisp.dom.style.overflow = 'hidden';
44642         
44643         this.footDisp.dom.innerHTML = html;
44644             
44645         //this.editorsyncValue();
44646     },
44647      
44648     
44649    
44650        
44651     // private
44652     onDestroy : function(){
44653         if(this.rendered){
44654             
44655             this.tb.items.each(function(item){
44656                 if(item.menu){
44657                     item.menu.removeAll();
44658                     if(item.menu.el){
44659                         item.menu.el.destroy();
44660                     }
44661                 }
44662                 item.destroy();
44663             });
44664              
44665         }
44666     },
44667     onFirstFocus: function() {
44668         // need to do this for all the toolbars..
44669         this.tb.items.each(function(item){
44670            item.enable();
44671         });
44672     },
44673     buildToolbar: function(tlist, nm)
44674     {
44675         var editor = this.editor;
44676         var editorcore = this.editorcore;
44677          // create a new element.
44678         var wdiv = editor.wrap.createChild({
44679                 tag: 'div'
44680             }, editor.wrap.dom.firstChild.nextSibling, true);
44681         
44682        
44683         var tb = new Roo.Toolbar(wdiv);
44684         // add the name..
44685         
44686         tb.add(nm+ ":&nbsp;");
44687         
44688         var styles = [];
44689         for(var i in this.styles) {
44690             styles.push(i);
44691         }
44692         
44693         // styles...
44694         if (styles && styles.length) {
44695             
44696             // this needs a multi-select checkbox...
44697             tb.addField( new Roo.form.ComboBox({
44698                 store: new Roo.data.SimpleStore({
44699                     id : 'val',
44700                     fields: ['val', 'selected'],
44701                     data : [] 
44702                 }),
44703                 name : '-roo-edit-className',
44704                 attrname : 'className',
44705                 displayField: 'val',
44706                 typeAhead: false,
44707                 mode: 'local',
44708                 editable : false,
44709                 triggerAction: 'all',
44710                 emptyText:'Select Style',
44711                 selectOnFocus:true,
44712                 width: 130,
44713                 listeners : {
44714                     'select': function(c, r, i) {
44715                         // initial support only for on class per el..
44716                         tb.selectedNode.className =  r ? r.get('val') : '';
44717                         editorcore.syncValue();
44718                     }
44719                 }
44720     
44721             }));
44722         }
44723         
44724         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44725         var tbops = tbc.options;
44726         
44727         for (var i in tlist) {
44728             
44729             var item = tlist[i];
44730             tb.add(item.title + ":&nbsp;");
44731             
44732             
44733             //optname == used so you can configure the options available..
44734             var opts = item.opts ? item.opts : false;
44735             if (item.optname) {
44736                 opts = tbops[item.optname];
44737            
44738             }
44739             
44740             if (opts) {
44741                 // opts == pulldown..
44742                 tb.addField( new Roo.form.ComboBox({
44743                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44744                         id : 'val',
44745                         fields: ['val', 'display'],
44746                         data : opts  
44747                     }),
44748                     name : '-roo-edit-' + i,
44749                     attrname : i,
44750                     stylename : item.style ? item.style : false,
44751                     displayField: item.displayField ? item.displayField : 'val',
44752                     valueField :  'val',
44753                     typeAhead: false,
44754                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44755                     editable : false,
44756                     triggerAction: 'all',
44757                     emptyText:'Select',
44758                     selectOnFocus:true,
44759                     width: item.width ? item.width  : 130,
44760                     listeners : {
44761                         'select': function(c, r, i) {
44762                             if (c.stylename) {
44763                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44764                                 return;
44765                             }
44766                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44767                         }
44768                     }
44769
44770                 }));
44771                 continue;
44772                     
44773                  
44774                 
44775                 tb.addField( new Roo.form.TextField({
44776                     name: i,
44777                     width: 100,
44778                     //allowBlank:false,
44779                     value: ''
44780                 }));
44781                 continue;
44782             }
44783             tb.addField( new Roo.form.TextField({
44784                 name: '-roo-edit-' + i,
44785                 attrname : i,
44786                 
44787                 width: item.width,
44788                 //allowBlank:true,
44789                 value: '',
44790                 listeners: {
44791                     'change' : function(f, nv, ov) {
44792                         tb.selectedNode.setAttribute(f.attrname, nv);
44793                     }
44794                 }
44795             }));
44796              
44797         }
44798         
44799         var _this = this;
44800         
44801         if(nm == 'BODY'){
44802             tb.addSeparator();
44803         
44804             tb.addButton( {
44805                 text: 'Stylesheets',
44806
44807                 listeners : {
44808                     click : function ()
44809                     {
44810                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44811                     }
44812                 }
44813             });
44814         }
44815         
44816         tb.addFill();
44817         tb.addButton( {
44818             text: 'Remove Tag',
44819     
44820             listeners : {
44821                 click : function ()
44822                 {
44823                     // remove
44824                     // undo does not work.
44825                      
44826                     var sn = tb.selectedNode;
44827                     
44828                     var pn = sn.parentNode;
44829                     
44830                     var stn =  sn.childNodes[0];
44831                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44832                     while (sn.childNodes.length) {
44833                         var node = sn.childNodes[0];
44834                         sn.removeChild(node);
44835                         //Roo.log(node);
44836                         pn.insertBefore(node, sn);
44837                         
44838                     }
44839                     pn.removeChild(sn);
44840                     var range = editorcore.createRange();
44841         
44842                     range.setStart(stn,0);
44843                     range.setEnd(en,0); //????
44844                     //range.selectNode(sel);
44845                     
44846                     
44847                     var selection = editorcore.getSelection();
44848                     selection.removeAllRanges();
44849                     selection.addRange(range);
44850                     
44851                     
44852                     
44853                     //_this.updateToolbar(null, null, pn);
44854                     _this.updateToolbar(null, null, null);
44855                     _this.footDisp.dom.innerHTML = ''; 
44856                 }
44857             }
44858             
44859                     
44860                 
44861             
44862         });
44863         
44864         
44865         tb.el.on('click', function(e){
44866             e.preventDefault(); // what does this do?
44867         });
44868         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44869         tb.el.hide();
44870         tb.name = nm;
44871         // dont need to disable them... as they will get hidden
44872         return tb;
44873          
44874         
44875     },
44876     buildFooter : function()
44877     {
44878         
44879         var fel = this.editor.wrap.createChild();
44880         this.footer = new Roo.Toolbar(fel);
44881         // toolbar has scrolly on left / right?
44882         var footDisp= new Roo.Toolbar.Fill();
44883         var _t = this;
44884         this.footer.add(
44885             {
44886                 text : '&lt;',
44887                 xtype: 'Button',
44888                 handler : function() {
44889                     _t.footDisp.scrollTo('left',0,true)
44890                 }
44891             }
44892         );
44893         this.footer.add( footDisp );
44894         this.footer.add( 
44895             {
44896                 text : '&gt;',
44897                 xtype: 'Button',
44898                 handler : function() {
44899                     // no animation..
44900                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44901                 }
44902             }
44903         );
44904         var fel = Roo.get(footDisp.el);
44905         fel.addClass('x-editor-context');
44906         this.footDispWrap = fel; 
44907         this.footDispWrap.overflow  = 'hidden';
44908         
44909         this.footDisp = fel.createChild();
44910         this.footDispWrap.on('click', this.onContextClick, this)
44911         
44912         
44913     },
44914     onContextClick : function (ev,dom)
44915     {
44916         ev.preventDefault();
44917         var  cn = dom.className;
44918         //Roo.log(cn);
44919         if (!cn.match(/x-ed-loc-/)) {
44920             return;
44921         }
44922         var n = cn.split('-').pop();
44923         var ans = this.footerEls;
44924         var sel = ans[n];
44925         
44926          // pick
44927         var range = this.editorcore.createRange();
44928         
44929         range.selectNodeContents(sel);
44930         //range.selectNode(sel);
44931         
44932         
44933         var selection = this.editorcore.getSelection();
44934         selection.removeAllRanges();
44935         selection.addRange(range);
44936         
44937         
44938         
44939         this.updateToolbar(null, null, sel);
44940         
44941         
44942     }
44943     
44944     
44945     
44946     
44947     
44948 });
44949
44950
44951
44952
44953
44954 /*
44955  * Based on:
44956  * Ext JS Library 1.1.1
44957  * Copyright(c) 2006-2007, Ext JS, LLC.
44958  *
44959  * Originally Released Under LGPL - original licence link has changed is not relivant.
44960  *
44961  * Fork - LGPL
44962  * <script type="text/javascript">
44963  */
44964  
44965 /**
44966  * @class Roo.form.BasicForm
44967  * @extends Roo.util.Observable
44968  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44969  * @constructor
44970  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44971  * @param {Object} config Configuration options
44972  */
44973 Roo.form.BasicForm = function(el, config){
44974     this.allItems = [];
44975     this.childForms = [];
44976     Roo.apply(this, config);
44977     /*
44978      * The Roo.form.Field items in this form.
44979      * @type MixedCollection
44980      */
44981      
44982      
44983     this.items = new Roo.util.MixedCollection(false, function(o){
44984         return o.id || (o.id = Roo.id());
44985     });
44986     this.addEvents({
44987         /**
44988          * @event beforeaction
44989          * Fires before any action is performed. Return false to cancel the action.
44990          * @param {Form} this
44991          * @param {Action} action The action to be performed
44992          */
44993         beforeaction: true,
44994         /**
44995          * @event actionfailed
44996          * Fires when an action fails.
44997          * @param {Form} this
44998          * @param {Action} action The action that failed
44999          */
45000         actionfailed : true,
45001         /**
45002          * @event actioncomplete
45003          * Fires when an action is completed.
45004          * @param {Form} this
45005          * @param {Action} action The action that completed
45006          */
45007         actioncomplete : true
45008     });
45009     if(el){
45010         this.initEl(el);
45011     }
45012     Roo.form.BasicForm.superclass.constructor.call(this);
45013 };
45014
45015 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45016     /**
45017      * @cfg {String} method
45018      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45019      */
45020     /**
45021      * @cfg {DataReader} reader
45022      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45023      * This is optional as there is built-in support for processing JSON.
45024      */
45025     /**
45026      * @cfg {DataReader} errorReader
45027      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45028      * This is completely optional as there is built-in support for processing JSON.
45029      */
45030     /**
45031      * @cfg {String} url
45032      * The URL to use for form actions if one isn't supplied in the action options.
45033      */
45034     /**
45035      * @cfg {Boolean} fileUpload
45036      * Set to true if this form is a file upload.
45037      */
45038      
45039     /**
45040      * @cfg {Object} baseParams
45041      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45042      */
45043      /**
45044      
45045     /**
45046      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45047      */
45048     timeout: 30,
45049
45050     // private
45051     activeAction : null,
45052
45053     /**
45054      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45055      * or setValues() data instead of when the form was first created.
45056      */
45057     trackResetOnLoad : false,
45058     
45059     
45060     /**
45061      * childForms - used for multi-tab forms
45062      * @type {Array}
45063      */
45064     childForms : false,
45065     
45066     /**
45067      * allItems - full list of fields.
45068      * @type {Array}
45069      */
45070     allItems : false,
45071     
45072     /**
45073      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45074      * element by passing it or its id or mask the form itself by passing in true.
45075      * @type Mixed
45076      */
45077     waitMsgTarget : false,
45078
45079     // private
45080     initEl : function(el){
45081         this.el = Roo.get(el);
45082         this.id = this.el.id || Roo.id();
45083         this.el.on('submit', this.onSubmit, this);
45084         this.el.addClass('x-form');
45085     },
45086
45087     // private
45088     onSubmit : function(e){
45089         e.stopEvent();
45090     },
45091
45092     /**
45093      * Returns true if client-side validation on the form is successful.
45094      * @return Boolean
45095      */
45096     isValid : function(){
45097         var valid = true;
45098         this.items.each(function(f){
45099            if(!f.validate()){
45100                valid = false;
45101            }
45102         });
45103         return valid;
45104     },
45105
45106     /**
45107      * Returns true if any fields in this form have changed since their original load.
45108      * @return Boolean
45109      */
45110     isDirty : function(){
45111         var dirty = false;
45112         this.items.each(function(f){
45113            if(f.isDirty()){
45114                dirty = true;
45115                return false;
45116            }
45117         });
45118         return dirty;
45119     },
45120
45121     /**
45122      * Performs a predefined action (submit or load) or custom actions you define on this form.
45123      * @param {String} actionName The name of the action type
45124      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45125      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45126      * accept other config options):
45127      * <pre>
45128 Property          Type             Description
45129 ----------------  ---------------  ----------------------------------------------------------------------------------
45130 url               String           The url for the action (defaults to the form's url)
45131 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45132 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45133 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45134                                    validate the form on the client (defaults to false)
45135      * </pre>
45136      * @return {BasicForm} this
45137      */
45138     doAction : function(action, options){
45139         if(typeof action == 'string'){
45140             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45141         }
45142         if(this.fireEvent('beforeaction', this, action) !== false){
45143             this.beforeAction(action);
45144             action.run.defer(100, action);
45145         }
45146         return this;
45147     },
45148
45149     /**
45150      * Shortcut to do a submit action.
45151      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45152      * @return {BasicForm} this
45153      */
45154     submit : function(options){
45155         this.doAction('submit', options);
45156         return this;
45157     },
45158
45159     /**
45160      * Shortcut to do a load action.
45161      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45162      * @return {BasicForm} this
45163      */
45164     load : function(options){
45165         this.doAction('load', options);
45166         return this;
45167     },
45168
45169     /**
45170      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45171      * @param {Record} record The record to edit
45172      * @return {BasicForm} this
45173      */
45174     updateRecord : function(record){
45175         record.beginEdit();
45176         var fs = record.fields;
45177         fs.each(function(f){
45178             var field = this.findField(f.name);
45179             if(field){
45180                 record.set(f.name, field.getValue());
45181             }
45182         }, this);
45183         record.endEdit();
45184         return this;
45185     },
45186
45187     /**
45188      * Loads an Roo.data.Record into this form.
45189      * @param {Record} record The record to load
45190      * @return {BasicForm} this
45191      */
45192     loadRecord : function(record){
45193         this.setValues(record.data);
45194         return this;
45195     },
45196
45197     // private
45198     beforeAction : function(action){
45199         var o = action.options;
45200         
45201        
45202         if(this.waitMsgTarget === true){
45203             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45204         }else if(this.waitMsgTarget){
45205             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45206             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45207         }else {
45208             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45209         }
45210          
45211     },
45212
45213     // private
45214     afterAction : function(action, success){
45215         this.activeAction = null;
45216         var o = action.options;
45217         
45218         if(this.waitMsgTarget === true){
45219             this.el.unmask();
45220         }else if(this.waitMsgTarget){
45221             this.waitMsgTarget.unmask();
45222         }else{
45223             Roo.MessageBox.updateProgress(1);
45224             Roo.MessageBox.hide();
45225         }
45226          
45227         if(success){
45228             if(o.reset){
45229                 this.reset();
45230             }
45231             Roo.callback(o.success, o.scope, [this, action]);
45232             this.fireEvent('actioncomplete', this, action);
45233             
45234         }else{
45235             
45236             // failure condition..
45237             // we have a scenario where updates need confirming.
45238             // eg. if a locking scenario exists..
45239             // we look for { errors : { needs_confirm : true }} in the response.
45240             if (
45241                 (typeof(action.result) != 'undefined')  &&
45242                 (typeof(action.result.errors) != 'undefined')  &&
45243                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45244            ){
45245                 var _t = this;
45246                 Roo.MessageBox.confirm(
45247                     "Change requires confirmation",
45248                     action.result.errorMsg,
45249                     function(r) {
45250                         if (r != 'yes') {
45251                             return;
45252                         }
45253                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45254                     }
45255                     
45256                 );
45257                 
45258                 
45259                 
45260                 return;
45261             }
45262             
45263             Roo.callback(o.failure, o.scope, [this, action]);
45264             // show an error message if no failed handler is set..
45265             if (!this.hasListener('actionfailed')) {
45266                 Roo.MessageBox.alert("Error",
45267                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45268                         action.result.errorMsg :
45269                         "Saving Failed, please check your entries or try again"
45270                 );
45271             }
45272             
45273             this.fireEvent('actionfailed', this, action);
45274         }
45275         
45276     },
45277
45278     /**
45279      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45280      * @param {String} id The value to search for
45281      * @return Field
45282      */
45283     findField : function(id){
45284         var field = this.items.get(id);
45285         if(!field){
45286             this.items.each(function(f){
45287                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45288                     field = f;
45289                     return false;
45290                 }
45291             });
45292         }
45293         return field || null;
45294     },
45295
45296     /**
45297      * Add a secondary form to this one, 
45298      * Used to provide tabbed forms. One form is primary, with hidden values 
45299      * which mirror the elements from the other forms.
45300      * 
45301      * @param {Roo.form.Form} form to add.
45302      * 
45303      */
45304     addForm : function(form)
45305     {
45306        
45307         if (this.childForms.indexOf(form) > -1) {
45308             // already added..
45309             return;
45310         }
45311         this.childForms.push(form);
45312         var n = '';
45313         Roo.each(form.allItems, function (fe) {
45314             
45315             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45316             if (this.findField(n)) { // already added..
45317                 return;
45318             }
45319             var add = new Roo.form.Hidden({
45320                 name : n
45321             });
45322             add.render(this.el);
45323             
45324             this.add( add );
45325         }, this);
45326         
45327     },
45328     /**
45329      * Mark fields in this form invalid in bulk.
45330      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45331      * @return {BasicForm} this
45332      */
45333     markInvalid : function(errors){
45334         if(errors instanceof Array){
45335             for(var i = 0, len = errors.length; i < len; i++){
45336                 var fieldError = errors[i];
45337                 var f = this.findField(fieldError.id);
45338                 if(f){
45339                     f.markInvalid(fieldError.msg);
45340                 }
45341             }
45342         }else{
45343             var field, id;
45344             for(id in errors){
45345                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45346                     field.markInvalid(errors[id]);
45347                 }
45348             }
45349         }
45350         Roo.each(this.childForms || [], function (f) {
45351             f.markInvalid(errors);
45352         });
45353         
45354         return this;
45355     },
45356
45357     /**
45358      * Set values for fields in this form in bulk.
45359      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45360      * @return {BasicForm} this
45361      */
45362     setValues : function(values){
45363         if(values instanceof Array){ // array of objects
45364             for(var i = 0, len = values.length; i < len; i++){
45365                 var v = values[i];
45366                 var f = this.findField(v.id);
45367                 if(f){
45368                     f.setValue(v.value);
45369                     if(this.trackResetOnLoad){
45370                         f.originalValue = f.getValue();
45371                     }
45372                 }
45373             }
45374         }else{ // object hash
45375             var field, id;
45376             for(id in values){
45377                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45378                     
45379                     if (field.setFromData && 
45380                         field.valueField && 
45381                         field.displayField &&
45382                         // combos' with local stores can 
45383                         // be queried via setValue()
45384                         // to set their value..
45385                         (field.store && !field.store.isLocal)
45386                         ) {
45387                         // it's a combo
45388                         var sd = { };
45389                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45390                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45391                         field.setFromData(sd);
45392                         
45393                     } else {
45394                         field.setValue(values[id]);
45395                     }
45396                     
45397                     
45398                     if(this.trackResetOnLoad){
45399                         field.originalValue = field.getValue();
45400                     }
45401                 }
45402             }
45403         }
45404          
45405         Roo.each(this.childForms || [], function (f) {
45406             f.setValues(values);
45407         });
45408                 
45409         return this;
45410     },
45411
45412     /**
45413      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45414      * they are returned as an array.
45415      * @param {Boolean} asString
45416      * @return {Object}
45417      */
45418     getValues : function(asString){
45419         if (this.childForms) {
45420             // copy values from the child forms
45421             Roo.each(this.childForms, function (f) {
45422                 this.setValues(f.getValues());
45423             }, this);
45424         }
45425         
45426         
45427         
45428         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45429         if(asString === true){
45430             return fs;
45431         }
45432         return Roo.urlDecode(fs);
45433     },
45434     
45435     /**
45436      * Returns the fields in this form as an object with key/value pairs. 
45437      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45438      * @return {Object}
45439      */
45440     getFieldValues : function(with_hidden)
45441     {
45442         if (this.childForms) {
45443             // copy values from the child forms
45444             // should this call getFieldValues - probably not as we do not currently copy
45445             // hidden fields when we generate..
45446             Roo.each(this.childForms, function (f) {
45447                 this.setValues(f.getValues());
45448             }, this);
45449         }
45450         
45451         var ret = {};
45452         this.items.each(function(f){
45453             if (!f.getName()) {
45454                 return;
45455             }
45456             var v = f.getValue();
45457             if (f.inputType =='radio') {
45458                 if (typeof(ret[f.getName()]) == 'undefined') {
45459                     ret[f.getName()] = ''; // empty..
45460                 }
45461                 
45462                 if (!f.el.dom.checked) {
45463                     return;
45464                     
45465                 }
45466                 v = f.el.dom.value;
45467                 
45468             }
45469             
45470             // not sure if this supported any more..
45471             if ((typeof(v) == 'object') && f.getRawValue) {
45472                 v = f.getRawValue() ; // dates..
45473             }
45474             // combo boxes where name != hiddenName...
45475             if (f.name != f.getName()) {
45476                 ret[f.name] = f.getRawValue();
45477             }
45478             ret[f.getName()] = v;
45479         });
45480         
45481         return ret;
45482     },
45483
45484     /**
45485      * Clears all invalid messages in this form.
45486      * @return {BasicForm} this
45487      */
45488     clearInvalid : function(){
45489         this.items.each(function(f){
45490            f.clearInvalid();
45491         });
45492         
45493         Roo.each(this.childForms || [], function (f) {
45494             f.clearInvalid();
45495         });
45496         
45497         
45498         return this;
45499     },
45500
45501     /**
45502      * Resets this form.
45503      * @return {BasicForm} this
45504      */
45505     reset : function(){
45506         this.items.each(function(f){
45507             f.reset();
45508         });
45509         
45510         Roo.each(this.childForms || [], function (f) {
45511             f.reset();
45512         });
45513        
45514         
45515         return this;
45516     },
45517
45518     /**
45519      * Add Roo.form components to this form.
45520      * @param {Field} field1
45521      * @param {Field} field2 (optional)
45522      * @param {Field} etc (optional)
45523      * @return {BasicForm} this
45524      */
45525     add : function(){
45526         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45527         return this;
45528     },
45529
45530
45531     /**
45532      * Removes a field from the items collection (does NOT remove its markup).
45533      * @param {Field} field
45534      * @return {BasicForm} this
45535      */
45536     remove : function(field){
45537         this.items.remove(field);
45538         return this;
45539     },
45540
45541     /**
45542      * Looks at the fields in this form, checks them for an id attribute,
45543      * and calls applyTo on the existing dom element with that id.
45544      * @return {BasicForm} this
45545      */
45546     render : function(){
45547         this.items.each(function(f){
45548             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45549                 f.applyTo(f.id);
45550             }
45551         });
45552         return this;
45553     },
45554
45555     /**
45556      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45557      * @param {Object} values
45558      * @return {BasicForm} this
45559      */
45560     applyToFields : function(o){
45561         this.items.each(function(f){
45562            Roo.apply(f, o);
45563         });
45564         return this;
45565     },
45566
45567     /**
45568      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45569      * @param {Object} values
45570      * @return {BasicForm} this
45571      */
45572     applyIfToFields : function(o){
45573         this.items.each(function(f){
45574            Roo.applyIf(f, o);
45575         });
45576         return this;
45577     }
45578 });
45579
45580 // back compat
45581 Roo.BasicForm = Roo.form.BasicForm;/*
45582  * Based on:
45583  * Ext JS Library 1.1.1
45584  * Copyright(c) 2006-2007, Ext JS, LLC.
45585  *
45586  * Originally Released Under LGPL - original licence link has changed is not relivant.
45587  *
45588  * Fork - LGPL
45589  * <script type="text/javascript">
45590  */
45591
45592 /**
45593  * @class Roo.form.Form
45594  * @extends Roo.form.BasicForm
45595  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45596  * @constructor
45597  * @param {Object} config Configuration options
45598  */
45599 Roo.form.Form = function(config){
45600     var xitems =  [];
45601     if (config.items) {
45602         xitems = config.items;
45603         delete config.items;
45604     }
45605    
45606     
45607     Roo.form.Form.superclass.constructor.call(this, null, config);
45608     this.url = this.url || this.action;
45609     if(!this.root){
45610         this.root = new Roo.form.Layout(Roo.applyIf({
45611             id: Roo.id()
45612         }, config));
45613     }
45614     this.active = this.root;
45615     /**
45616      * Array of all the buttons that have been added to this form via {@link addButton}
45617      * @type Array
45618      */
45619     this.buttons = [];
45620     this.allItems = [];
45621     this.addEvents({
45622         /**
45623          * @event clientvalidation
45624          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45625          * @param {Form} this
45626          * @param {Boolean} valid true if the form has passed client-side validation
45627          */
45628         clientvalidation: true,
45629         /**
45630          * @event rendered
45631          * Fires when the form is rendered
45632          * @param {Roo.form.Form} form
45633          */
45634         rendered : true
45635     });
45636     
45637     if (this.progressUrl) {
45638             // push a hidden field onto the list of fields..
45639             this.addxtype( {
45640                     xns: Roo.form, 
45641                     xtype : 'Hidden', 
45642                     name : 'UPLOAD_IDENTIFIER' 
45643             });
45644         }
45645         
45646     
45647     Roo.each(xitems, this.addxtype, this);
45648     
45649     
45650     
45651 };
45652
45653 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45654     /**
45655      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45656      */
45657     /**
45658      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45659      */
45660     /**
45661      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45662      */
45663     buttonAlign:'center',
45664
45665     /**
45666      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45667      */
45668     minButtonWidth:75,
45669
45670     /**
45671      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45672      * This property cascades to child containers if not set.
45673      */
45674     labelAlign:'left',
45675
45676     /**
45677      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45678      * fires a looping event with that state. This is required to bind buttons to the valid
45679      * state using the config value formBind:true on the button.
45680      */
45681     monitorValid : false,
45682
45683     /**
45684      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45685      */
45686     monitorPoll : 200,
45687     
45688     /**
45689      * @cfg {String} progressUrl - Url to return progress data 
45690      */
45691     
45692     progressUrl : false,
45693   
45694     /**
45695      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45696      * fields are added and the column is closed. If no fields are passed the column remains open
45697      * until end() is called.
45698      * @param {Object} config The config to pass to the column
45699      * @param {Field} field1 (optional)
45700      * @param {Field} field2 (optional)
45701      * @param {Field} etc (optional)
45702      * @return Column The column container object
45703      */
45704     column : function(c){
45705         var col = new Roo.form.Column(c);
45706         this.start(col);
45707         if(arguments.length > 1){ // duplicate code required because of Opera
45708             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45709             this.end();
45710         }
45711         return col;
45712     },
45713
45714     /**
45715      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45716      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45717      * until end() is called.
45718      * @param {Object} config The config to pass to the fieldset
45719      * @param {Field} field1 (optional)
45720      * @param {Field} field2 (optional)
45721      * @param {Field} etc (optional)
45722      * @return FieldSet The fieldset container object
45723      */
45724     fieldset : function(c){
45725         var fs = new Roo.form.FieldSet(c);
45726         this.start(fs);
45727         if(arguments.length > 1){ // duplicate code required because of Opera
45728             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45729             this.end();
45730         }
45731         return fs;
45732     },
45733
45734     /**
45735      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45736      * fields are added and the container is closed. If no fields are passed the container remains open
45737      * until end() is called.
45738      * @param {Object} config The config to pass to the Layout
45739      * @param {Field} field1 (optional)
45740      * @param {Field} field2 (optional)
45741      * @param {Field} etc (optional)
45742      * @return Layout The container object
45743      */
45744     container : function(c){
45745         var l = new Roo.form.Layout(c);
45746         this.start(l);
45747         if(arguments.length > 1){ // duplicate code required because of Opera
45748             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45749             this.end();
45750         }
45751         return l;
45752     },
45753
45754     /**
45755      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45756      * @param {Object} container A Roo.form.Layout or subclass of Layout
45757      * @return {Form} this
45758      */
45759     start : function(c){
45760         // cascade label info
45761         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45762         this.active.stack.push(c);
45763         c.ownerCt = this.active;
45764         this.active = c;
45765         return this;
45766     },
45767
45768     /**
45769      * Closes the current open container
45770      * @return {Form} this
45771      */
45772     end : function(){
45773         if(this.active == this.root){
45774             return this;
45775         }
45776         this.active = this.active.ownerCt;
45777         return this;
45778     },
45779
45780     /**
45781      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45782      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45783      * as the label of the field.
45784      * @param {Field} field1
45785      * @param {Field} field2 (optional)
45786      * @param {Field} etc. (optional)
45787      * @return {Form} this
45788      */
45789     add : function(){
45790         this.active.stack.push.apply(this.active.stack, arguments);
45791         this.allItems.push.apply(this.allItems,arguments);
45792         var r = [];
45793         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45794             if(a[i].isFormField){
45795                 r.push(a[i]);
45796             }
45797         }
45798         if(r.length > 0){
45799             Roo.form.Form.superclass.add.apply(this, r);
45800         }
45801         return this;
45802     },
45803     
45804
45805     
45806     
45807     
45808      /**
45809      * Find any element that has been added to a form, using it's ID or name
45810      * This can include framesets, columns etc. along with regular fields..
45811      * @param {String} id - id or name to find.
45812      
45813      * @return {Element} e - or false if nothing found.
45814      */
45815     findbyId : function(id)
45816     {
45817         var ret = false;
45818         if (!id) {
45819             return ret;
45820         }
45821         Roo.each(this.allItems, function(f){
45822             if (f.id == id || f.name == id ){
45823                 ret = f;
45824                 return false;
45825             }
45826         });
45827         return ret;
45828     },
45829
45830     
45831     
45832     /**
45833      * Render this form into the passed container. This should only be called once!
45834      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45835      * @return {Form} this
45836      */
45837     render : function(ct)
45838     {
45839         
45840         
45841         
45842         ct = Roo.get(ct);
45843         var o = this.autoCreate || {
45844             tag: 'form',
45845             method : this.method || 'POST',
45846             id : this.id || Roo.id()
45847         };
45848         this.initEl(ct.createChild(o));
45849
45850         this.root.render(this.el);
45851         
45852        
45853              
45854         this.items.each(function(f){
45855             f.render('x-form-el-'+f.id);
45856         });
45857
45858         if(this.buttons.length > 0){
45859             // tables are required to maintain order and for correct IE layout
45860             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45861                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45862                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45863             }}, null, true);
45864             var tr = tb.getElementsByTagName('tr')[0];
45865             for(var i = 0, len = this.buttons.length; i < len; i++) {
45866                 var b = this.buttons[i];
45867                 var td = document.createElement('td');
45868                 td.className = 'x-form-btn-td';
45869                 b.render(tr.appendChild(td));
45870             }
45871         }
45872         if(this.monitorValid){ // initialize after render
45873             this.startMonitoring();
45874         }
45875         this.fireEvent('rendered', this);
45876         return this;
45877     },
45878
45879     /**
45880      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45881      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45882      * object or a valid Roo.DomHelper element config
45883      * @param {Function} handler The function called when the button is clicked
45884      * @param {Object} scope (optional) The scope of the handler function
45885      * @return {Roo.Button}
45886      */
45887     addButton : function(config, handler, scope){
45888         var bc = {
45889             handler: handler,
45890             scope: scope,
45891             minWidth: this.minButtonWidth,
45892             hideParent:true
45893         };
45894         if(typeof config == "string"){
45895             bc.text = config;
45896         }else{
45897             Roo.apply(bc, config);
45898         }
45899         var btn = new Roo.Button(null, bc);
45900         this.buttons.push(btn);
45901         return btn;
45902     },
45903
45904      /**
45905      * Adds a series of form elements (using the xtype property as the factory method.
45906      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45907      * @param {Object} config 
45908      */
45909     
45910     addxtype : function()
45911     {
45912         var ar = Array.prototype.slice.call(arguments, 0);
45913         var ret = false;
45914         for(var i = 0; i < ar.length; i++) {
45915             if (!ar[i]) {
45916                 continue; // skip -- if this happends something invalid got sent, we 
45917                 // should ignore it, as basically that interface element will not show up
45918                 // and that should be pretty obvious!!
45919             }
45920             
45921             if (Roo.form[ar[i].xtype]) {
45922                 ar[i].form = this;
45923                 var fe = Roo.factory(ar[i], Roo.form);
45924                 if (!ret) {
45925                     ret = fe;
45926                 }
45927                 fe.form = this;
45928                 if (fe.store) {
45929                     fe.store.form = this;
45930                 }
45931                 if (fe.isLayout) {  
45932                          
45933                     this.start(fe);
45934                     this.allItems.push(fe);
45935                     if (fe.items && fe.addxtype) {
45936                         fe.addxtype.apply(fe, fe.items);
45937                         delete fe.items;
45938                     }
45939                      this.end();
45940                     continue;
45941                 }
45942                 
45943                 
45944                  
45945                 this.add(fe);
45946               //  console.log('adding ' + ar[i].xtype);
45947             }
45948             if (ar[i].xtype == 'Button') {  
45949                 //console.log('adding button');
45950                 //console.log(ar[i]);
45951                 this.addButton(ar[i]);
45952                 this.allItems.push(fe);
45953                 continue;
45954             }
45955             
45956             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45957                 alert('end is not supported on xtype any more, use items');
45958             //    this.end();
45959             //    //console.log('adding end');
45960             }
45961             
45962         }
45963         return ret;
45964     },
45965     
45966     /**
45967      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45968      * option "monitorValid"
45969      */
45970     startMonitoring : function(){
45971         if(!this.bound){
45972             this.bound = true;
45973             Roo.TaskMgr.start({
45974                 run : this.bindHandler,
45975                 interval : this.monitorPoll || 200,
45976                 scope: this
45977             });
45978         }
45979     },
45980
45981     /**
45982      * Stops monitoring of the valid state of this form
45983      */
45984     stopMonitoring : function(){
45985         this.bound = false;
45986     },
45987
45988     // private
45989     bindHandler : function(){
45990         if(!this.bound){
45991             return false; // stops binding
45992         }
45993         var valid = true;
45994         this.items.each(function(f){
45995             if(!f.isValid(true)){
45996                 valid = false;
45997                 return false;
45998             }
45999         });
46000         for(var i = 0, len = this.buttons.length; i < len; i++){
46001             var btn = this.buttons[i];
46002             if(btn.formBind === true && btn.disabled === valid){
46003                 btn.setDisabled(!valid);
46004             }
46005         }
46006         this.fireEvent('clientvalidation', this, valid);
46007     }
46008     
46009     
46010     
46011     
46012     
46013     
46014     
46015     
46016 });
46017
46018
46019 // back compat
46020 Roo.Form = Roo.form.Form;
46021 /*
46022  * Based on:
46023  * Ext JS Library 1.1.1
46024  * Copyright(c) 2006-2007, Ext JS, LLC.
46025  *
46026  * Originally Released Under LGPL - original licence link has changed is not relivant.
46027  *
46028  * Fork - LGPL
46029  * <script type="text/javascript">
46030  */
46031
46032 // as we use this in bootstrap.
46033 Roo.namespace('Roo.form');
46034  /**
46035  * @class Roo.form.Action
46036  * Internal Class used to handle form actions
46037  * @constructor
46038  * @param {Roo.form.BasicForm} el The form element or its id
46039  * @param {Object} config Configuration options
46040  */
46041
46042  
46043  
46044 // define the action interface
46045 Roo.form.Action = function(form, options){
46046     this.form = form;
46047     this.options = options || {};
46048 };
46049 /**
46050  * Client Validation Failed
46051  * @const 
46052  */
46053 Roo.form.Action.CLIENT_INVALID = 'client';
46054 /**
46055  * Server Validation Failed
46056  * @const 
46057  */
46058 Roo.form.Action.SERVER_INVALID = 'server';
46059  /**
46060  * Connect to Server Failed
46061  * @const 
46062  */
46063 Roo.form.Action.CONNECT_FAILURE = 'connect';
46064 /**
46065  * Reading Data from Server Failed
46066  * @const 
46067  */
46068 Roo.form.Action.LOAD_FAILURE = 'load';
46069
46070 Roo.form.Action.prototype = {
46071     type : 'default',
46072     failureType : undefined,
46073     response : undefined,
46074     result : undefined,
46075
46076     // interface method
46077     run : function(options){
46078
46079     },
46080
46081     // interface method
46082     success : function(response){
46083
46084     },
46085
46086     // interface method
46087     handleResponse : function(response){
46088
46089     },
46090
46091     // default connection failure
46092     failure : function(response){
46093         
46094         this.response = response;
46095         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46096         this.form.afterAction(this, false);
46097     },
46098
46099     processResponse : function(response){
46100         this.response = response;
46101         if(!response.responseText){
46102             return true;
46103         }
46104         this.result = this.handleResponse(response);
46105         return this.result;
46106     },
46107
46108     // utility functions used internally
46109     getUrl : function(appendParams){
46110         var url = this.options.url || this.form.url || this.form.el.dom.action;
46111         if(appendParams){
46112             var p = this.getParams();
46113             if(p){
46114                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46115             }
46116         }
46117         return url;
46118     },
46119
46120     getMethod : function(){
46121         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46122     },
46123
46124     getParams : function(){
46125         var bp = this.form.baseParams;
46126         var p = this.options.params;
46127         if(p){
46128             if(typeof p == "object"){
46129                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46130             }else if(typeof p == 'string' && bp){
46131                 p += '&' + Roo.urlEncode(bp);
46132             }
46133         }else if(bp){
46134             p = Roo.urlEncode(bp);
46135         }
46136         return p;
46137     },
46138
46139     createCallback : function(){
46140         return {
46141             success: this.success,
46142             failure: this.failure,
46143             scope: this,
46144             timeout: (this.form.timeout*1000),
46145             upload: this.form.fileUpload ? this.success : undefined
46146         };
46147     }
46148 };
46149
46150 Roo.form.Action.Submit = function(form, options){
46151     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46152 };
46153
46154 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46155     type : 'submit',
46156
46157     haveProgress : false,
46158     uploadComplete : false,
46159     
46160     // uploadProgress indicator.
46161     uploadProgress : function()
46162     {
46163         if (!this.form.progressUrl) {
46164             return;
46165         }
46166         
46167         if (!this.haveProgress) {
46168             Roo.MessageBox.progress("Uploading", "Uploading");
46169         }
46170         if (this.uploadComplete) {
46171            Roo.MessageBox.hide();
46172            return;
46173         }
46174         
46175         this.haveProgress = true;
46176    
46177         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46178         
46179         var c = new Roo.data.Connection();
46180         c.request({
46181             url : this.form.progressUrl,
46182             params: {
46183                 id : uid
46184             },
46185             method: 'GET',
46186             success : function(req){
46187                //console.log(data);
46188                 var rdata = false;
46189                 var edata;
46190                 try  {
46191                    rdata = Roo.decode(req.responseText)
46192                 } catch (e) {
46193                     Roo.log("Invalid data from server..");
46194                     Roo.log(edata);
46195                     return;
46196                 }
46197                 if (!rdata || !rdata.success) {
46198                     Roo.log(rdata);
46199                     Roo.MessageBox.alert(Roo.encode(rdata));
46200                     return;
46201                 }
46202                 var data = rdata.data;
46203                 
46204                 if (this.uploadComplete) {
46205                    Roo.MessageBox.hide();
46206                    return;
46207                 }
46208                    
46209                 if (data){
46210                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46211                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46212                     );
46213                 }
46214                 this.uploadProgress.defer(2000,this);
46215             },
46216        
46217             failure: function(data) {
46218                 Roo.log('progress url failed ');
46219                 Roo.log(data);
46220             },
46221             scope : this
46222         });
46223            
46224     },
46225     
46226     
46227     run : function()
46228     {
46229         // run get Values on the form, so it syncs any secondary forms.
46230         this.form.getValues();
46231         
46232         var o = this.options;
46233         var method = this.getMethod();
46234         var isPost = method == 'POST';
46235         if(o.clientValidation === false || this.form.isValid()){
46236             
46237             if (this.form.progressUrl) {
46238                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46239                     (new Date() * 1) + '' + Math.random());
46240                     
46241             } 
46242             
46243             
46244             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46245                 form:this.form.el.dom,
46246                 url:this.getUrl(!isPost),
46247                 method: method,
46248                 params:isPost ? this.getParams() : null,
46249                 isUpload: this.form.fileUpload
46250             }));
46251             
46252             this.uploadProgress();
46253
46254         }else if (o.clientValidation !== false){ // client validation failed
46255             this.failureType = Roo.form.Action.CLIENT_INVALID;
46256             this.form.afterAction(this, false);
46257         }
46258     },
46259
46260     success : function(response)
46261     {
46262         this.uploadComplete= true;
46263         if (this.haveProgress) {
46264             Roo.MessageBox.hide();
46265         }
46266         
46267         
46268         var result = this.processResponse(response);
46269         if(result === true || result.success){
46270             this.form.afterAction(this, true);
46271             return;
46272         }
46273         if(result.errors){
46274             this.form.markInvalid(result.errors);
46275             this.failureType = Roo.form.Action.SERVER_INVALID;
46276         }
46277         this.form.afterAction(this, false);
46278     },
46279     failure : function(response)
46280     {
46281         this.uploadComplete= true;
46282         if (this.haveProgress) {
46283             Roo.MessageBox.hide();
46284         }
46285         
46286         this.response = response;
46287         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46288         this.form.afterAction(this, false);
46289     },
46290     
46291     handleResponse : function(response){
46292         if(this.form.errorReader){
46293             var rs = this.form.errorReader.read(response);
46294             var errors = [];
46295             if(rs.records){
46296                 for(var i = 0, len = rs.records.length; i < len; i++) {
46297                     var r = rs.records[i];
46298                     errors[i] = r.data;
46299                 }
46300             }
46301             if(errors.length < 1){
46302                 errors = null;
46303             }
46304             return {
46305                 success : rs.success,
46306                 errors : errors
46307             };
46308         }
46309         var ret = false;
46310         try {
46311             ret = Roo.decode(response.responseText);
46312         } catch (e) {
46313             ret = {
46314                 success: false,
46315                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46316                 errors : []
46317             };
46318         }
46319         return ret;
46320         
46321     }
46322 });
46323
46324
46325 Roo.form.Action.Load = function(form, options){
46326     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46327     this.reader = this.form.reader;
46328 };
46329
46330 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46331     type : 'load',
46332
46333     run : function(){
46334         
46335         Roo.Ajax.request(Roo.apply(
46336                 this.createCallback(), {
46337                     method:this.getMethod(),
46338                     url:this.getUrl(false),
46339                     params:this.getParams()
46340         }));
46341     },
46342
46343     success : function(response){
46344         
46345         var result = this.processResponse(response);
46346         if(result === true || !result.success || !result.data){
46347             this.failureType = Roo.form.Action.LOAD_FAILURE;
46348             this.form.afterAction(this, false);
46349             return;
46350         }
46351         this.form.clearInvalid();
46352         this.form.setValues(result.data);
46353         this.form.afterAction(this, true);
46354     },
46355
46356     handleResponse : function(response){
46357         if(this.form.reader){
46358             var rs = this.form.reader.read(response);
46359             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46360             return {
46361                 success : rs.success,
46362                 data : data
46363             };
46364         }
46365         return Roo.decode(response.responseText);
46366     }
46367 });
46368
46369 Roo.form.Action.ACTION_TYPES = {
46370     'load' : Roo.form.Action.Load,
46371     'submit' : Roo.form.Action.Submit
46372 };/*
46373  * Based on:
46374  * Ext JS Library 1.1.1
46375  * Copyright(c) 2006-2007, Ext JS, LLC.
46376  *
46377  * Originally Released Under LGPL - original licence link has changed is not relivant.
46378  *
46379  * Fork - LGPL
46380  * <script type="text/javascript">
46381  */
46382  
46383 /**
46384  * @class Roo.form.Layout
46385  * @extends Roo.Component
46386  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46387  * @constructor
46388  * @param {Object} config Configuration options
46389  */
46390 Roo.form.Layout = function(config){
46391     var xitems = [];
46392     if (config.items) {
46393         xitems = config.items;
46394         delete config.items;
46395     }
46396     Roo.form.Layout.superclass.constructor.call(this, config);
46397     this.stack = [];
46398     Roo.each(xitems, this.addxtype, this);
46399      
46400 };
46401
46402 Roo.extend(Roo.form.Layout, Roo.Component, {
46403     /**
46404      * @cfg {String/Object} autoCreate
46405      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46406      */
46407     /**
46408      * @cfg {String/Object/Function} style
46409      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46410      * a function which returns such a specification.
46411      */
46412     /**
46413      * @cfg {String} labelAlign
46414      * Valid values are "left," "top" and "right" (defaults to "left")
46415      */
46416     /**
46417      * @cfg {Number} labelWidth
46418      * Fixed width in pixels of all field labels (defaults to undefined)
46419      */
46420     /**
46421      * @cfg {Boolean} clear
46422      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46423      */
46424     clear : true,
46425     /**
46426      * @cfg {String} labelSeparator
46427      * The separator to use after field labels (defaults to ':')
46428      */
46429     labelSeparator : ':',
46430     /**
46431      * @cfg {Boolean} hideLabels
46432      * True to suppress the display of field labels in this layout (defaults to false)
46433      */
46434     hideLabels : false,
46435
46436     // private
46437     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46438     
46439     isLayout : true,
46440     
46441     // private
46442     onRender : function(ct, position){
46443         if(this.el){ // from markup
46444             this.el = Roo.get(this.el);
46445         }else {  // generate
46446             var cfg = this.getAutoCreate();
46447             this.el = ct.createChild(cfg, position);
46448         }
46449         if(this.style){
46450             this.el.applyStyles(this.style);
46451         }
46452         if(this.labelAlign){
46453             this.el.addClass('x-form-label-'+this.labelAlign);
46454         }
46455         if(this.hideLabels){
46456             this.labelStyle = "display:none";
46457             this.elementStyle = "padding-left:0;";
46458         }else{
46459             if(typeof this.labelWidth == 'number'){
46460                 this.labelStyle = "width:"+this.labelWidth+"px;";
46461                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46462             }
46463             if(this.labelAlign == 'top'){
46464                 this.labelStyle = "width:auto;";
46465                 this.elementStyle = "padding-left:0;";
46466             }
46467         }
46468         var stack = this.stack;
46469         var slen = stack.length;
46470         if(slen > 0){
46471             if(!this.fieldTpl){
46472                 var t = new Roo.Template(
46473                     '<div class="x-form-item {5}">',
46474                         '<label for="{0}" style="{2}">{1}{4}</label>',
46475                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46476                         '</div>',
46477                     '</div><div class="x-form-clear-left"></div>'
46478                 );
46479                 t.disableFormats = true;
46480                 t.compile();
46481                 Roo.form.Layout.prototype.fieldTpl = t;
46482             }
46483             for(var i = 0; i < slen; i++) {
46484                 if(stack[i].isFormField){
46485                     this.renderField(stack[i]);
46486                 }else{
46487                     this.renderComponent(stack[i]);
46488                 }
46489             }
46490         }
46491         if(this.clear){
46492             this.el.createChild({cls:'x-form-clear'});
46493         }
46494     },
46495
46496     // private
46497     renderField : function(f){
46498         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46499                f.id, //0
46500                f.fieldLabel, //1
46501                f.labelStyle||this.labelStyle||'', //2
46502                this.elementStyle||'', //3
46503                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46504                f.itemCls||this.itemCls||''  //5
46505        ], true).getPrevSibling());
46506     },
46507
46508     // private
46509     renderComponent : function(c){
46510         c.render(c.isLayout ? this.el : this.el.createChild());    
46511     },
46512     /**
46513      * Adds a object form elements (using the xtype property as the factory method.)
46514      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46515      * @param {Object} config 
46516      */
46517     addxtype : function(o)
46518     {
46519         // create the lement.
46520         o.form = this.form;
46521         var fe = Roo.factory(o, Roo.form);
46522         this.form.allItems.push(fe);
46523         this.stack.push(fe);
46524         
46525         if (fe.isFormField) {
46526             this.form.items.add(fe);
46527         }
46528          
46529         return fe;
46530     }
46531 });
46532
46533 /**
46534  * @class Roo.form.Column
46535  * @extends Roo.form.Layout
46536  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46537  * @constructor
46538  * @param {Object} config Configuration options
46539  */
46540 Roo.form.Column = function(config){
46541     Roo.form.Column.superclass.constructor.call(this, config);
46542 };
46543
46544 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46545     /**
46546      * @cfg {Number/String} width
46547      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46548      */
46549     /**
46550      * @cfg {String/Object} autoCreate
46551      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46552      */
46553
46554     // private
46555     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46556
46557     // private
46558     onRender : function(ct, position){
46559         Roo.form.Column.superclass.onRender.call(this, ct, position);
46560         if(this.width){
46561             this.el.setWidth(this.width);
46562         }
46563     }
46564 });
46565
46566
46567 /**
46568  * @class Roo.form.Row
46569  * @extends Roo.form.Layout
46570  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46571  * @constructor
46572  * @param {Object} config Configuration options
46573  */
46574
46575  
46576 Roo.form.Row = function(config){
46577     Roo.form.Row.superclass.constructor.call(this, config);
46578 };
46579  
46580 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46581       /**
46582      * @cfg {Number/String} width
46583      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46584      */
46585     /**
46586      * @cfg {Number/String} height
46587      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46588      */
46589     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46590     
46591     padWidth : 20,
46592     // private
46593     onRender : function(ct, position){
46594         //console.log('row render');
46595         if(!this.rowTpl){
46596             var t = new Roo.Template(
46597                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46598                     '<label for="{0}" style="{2}">{1}{4}</label>',
46599                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46600                     '</div>',
46601                 '</div>'
46602             );
46603             t.disableFormats = true;
46604             t.compile();
46605             Roo.form.Layout.prototype.rowTpl = t;
46606         }
46607         this.fieldTpl = this.rowTpl;
46608         
46609         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46610         var labelWidth = 100;
46611         
46612         if ((this.labelAlign != 'top')) {
46613             if (typeof this.labelWidth == 'number') {
46614                 labelWidth = this.labelWidth
46615             }
46616             this.padWidth =  20 + labelWidth;
46617             
46618         }
46619         
46620         Roo.form.Column.superclass.onRender.call(this, ct, position);
46621         if(this.width){
46622             this.el.setWidth(this.width);
46623         }
46624         if(this.height){
46625             this.el.setHeight(this.height);
46626         }
46627     },
46628     
46629     // private
46630     renderField : function(f){
46631         f.fieldEl = this.fieldTpl.append(this.el, [
46632                f.id, f.fieldLabel,
46633                f.labelStyle||this.labelStyle||'',
46634                this.elementStyle||'',
46635                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46636                f.itemCls||this.itemCls||'',
46637                f.width ? f.width + this.padWidth : 160 + this.padWidth
46638        ],true);
46639     }
46640 });
46641  
46642
46643 /**
46644  * @class Roo.form.FieldSet
46645  * @extends Roo.form.Layout
46646  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46647  * @constructor
46648  * @param {Object} config Configuration options
46649  */
46650 Roo.form.FieldSet = function(config){
46651     Roo.form.FieldSet.superclass.constructor.call(this, config);
46652 };
46653
46654 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46655     /**
46656      * @cfg {String} legend
46657      * The text to display as the legend for the FieldSet (defaults to '')
46658      */
46659     /**
46660      * @cfg {String/Object} autoCreate
46661      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46662      */
46663
46664     // private
46665     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46666
46667     // private
46668     onRender : function(ct, position){
46669         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46670         if(this.legend){
46671             this.setLegend(this.legend);
46672         }
46673     },
46674
46675     // private
46676     setLegend : function(text){
46677         if(this.rendered){
46678             this.el.child('legend').update(text);
46679         }
46680     }
46681 });/*
46682  * Based on:
46683  * Ext JS Library 1.1.1
46684  * Copyright(c) 2006-2007, Ext JS, LLC.
46685  *
46686  * Originally Released Under LGPL - original licence link has changed is not relivant.
46687  *
46688  * Fork - LGPL
46689  * <script type="text/javascript">
46690  */
46691 /**
46692  * @class Roo.form.VTypes
46693  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46694  * @singleton
46695  */
46696 Roo.form.VTypes = function(){
46697     // closure these in so they are only created once.
46698     var alpha = /^[a-zA-Z_]+$/;
46699     var alphanum = /^[a-zA-Z0-9_]+$/;
46700     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46701     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46702
46703     // All these messages and functions are configurable
46704     return {
46705         /**
46706          * The function used to validate email addresses
46707          * @param {String} value The email address
46708          */
46709         'email' : function(v){
46710             return email.test(v);
46711         },
46712         /**
46713          * The error text to display when the email validation function returns false
46714          * @type String
46715          */
46716         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46717         /**
46718          * The keystroke filter mask to be applied on email input
46719          * @type RegExp
46720          */
46721         'emailMask' : /[a-z0-9_\.\-@]/i,
46722
46723         /**
46724          * The function used to validate URLs
46725          * @param {String} value The URL
46726          */
46727         'url' : function(v){
46728             return url.test(v);
46729         },
46730         /**
46731          * The error text to display when the url validation function returns false
46732          * @type String
46733          */
46734         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46735         
46736         /**
46737          * The function used to validate alpha values
46738          * @param {String} value The value
46739          */
46740         'alpha' : function(v){
46741             return alpha.test(v);
46742         },
46743         /**
46744          * The error text to display when the alpha validation function returns false
46745          * @type String
46746          */
46747         'alphaText' : 'This field should only contain letters and _',
46748         /**
46749          * The keystroke filter mask to be applied on alpha input
46750          * @type RegExp
46751          */
46752         'alphaMask' : /[a-z_]/i,
46753
46754         /**
46755          * The function used to validate alphanumeric values
46756          * @param {String} value The value
46757          */
46758         'alphanum' : function(v){
46759             return alphanum.test(v);
46760         },
46761         /**
46762          * The error text to display when the alphanumeric validation function returns false
46763          * @type String
46764          */
46765         'alphanumText' : 'This field should only contain letters, numbers and _',
46766         /**
46767          * The keystroke filter mask to be applied on alphanumeric input
46768          * @type RegExp
46769          */
46770         'alphanumMask' : /[a-z0-9_]/i
46771     };
46772 }();//<script type="text/javascript">
46773
46774 /**
46775  * @class Roo.form.FCKeditor
46776  * @extends Roo.form.TextArea
46777  * Wrapper around the FCKEditor http://www.fckeditor.net
46778  * @constructor
46779  * Creates a new FCKeditor
46780  * @param {Object} config Configuration options
46781  */
46782 Roo.form.FCKeditor = function(config){
46783     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46784     this.addEvents({
46785          /**
46786          * @event editorinit
46787          * Fired when the editor is initialized - you can add extra handlers here..
46788          * @param {FCKeditor} this
46789          * @param {Object} the FCK object.
46790          */
46791         editorinit : true
46792     });
46793     
46794     
46795 };
46796 Roo.form.FCKeditor.editors = { };
46797 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46798 {
46799     //defaultAutoCreate : {
46800     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46801     //},
46802     // private
46803     /**
46804      * @cfg {Object} fck options - see fck manual for details.
46805      */
46806     fckconfig : false,
46807     
46808     /**
46809      * @cfg {Object} fck toolbar set (Basic or Default)
46810      */
46811     toolbarSet : 'Basic',
46812     /**
46813      * @cfg {Object} fck BasePath
46814      */ 
46815     basePath : '/fckeditor/',
46816     
46817     
46818     frame : false,
46819     
46820     value : '',
46821     
46822    
46823     onRender : function(ct, position)
46824     {
46825         if(!this.el){
46826             this.defaultAutoCreate = {
46827                 tag: "textarea",
46828                 style:"width:300px;height:60px;",
46829                 autocomplete: "new-password"
46830             };
46831         }
46832         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46833         /*
46834         if(this.grow){
46835             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46836             if(this.preventScrollbars){
46837                 this.el.setStyle("overflow", "hidden");
46838             }
46839             this.el.setHeight(this.growMin);
46840         }
46841         */
46842         //console.log('onrender' + this.getId() );
46843         Roo.form.FCKeditor.editors[this.getId()] = this;
46844          
46845
46846         this.replaceTextarea() ;
46847         
46848     },
46849     
46850     getEditor : function() {
46851         return this.fckEditor;
46852     },
46853     /**
46854      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46855      * @param {Mixed} value The value to set
46856      */
46857     
46858     
46859     setValue : function(value)
46860     {
46861         //console.log('setValue: ' + value);
46862         
46863         if(typeof(value) == 'undefined') { // not sure why this is happending...
46864             return;
46865         }
46866         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46867         
46868         //if(!this.el || !this.getEditor()) {
46869         //    this.value = value;
46870             //this.setValue.defer(100,this,[value]);    
46871         //    return;
46872         //} 
46873         
46874         if(!this.getEditor()) {
46875             return;
46876         }
46877         
46878         this.getEditor().SetData(value);
46879         
46880         //
46881
46882     },
46883
46884     /**
46885      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46886      * @return {Mixed} value The field value
46887      */
46888     getValue : function()
46889     {
46890         
46891         if (this.frame && this.frame.dom.style.display == 'none') {
46892             return Roo.form.FCKeditor.superclass.getValue.call(this);
46893         }
46894         
46895         if(!this.el || !this.getEditor()) {
46896            
46897            // this.getValue.defer(100,this); 
46898             return this.value;
46899         }
46900        
46901         
46902         var value=this.getEditor().GetData();
46903         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46904         return Roo.form.FCKeditor.superclass.getValue.call(this);
46905         
46906
46907     },
46908
46909     /**
46910      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46911      * @return {Mixed} value The field value
46912      */
46913     getRawValue : function()
46914     {
46915         if (this.frame && this.frame.dom.style.display == 'none') {
46916             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46917         }
46918         
46919         if(!this.el || !this.getEditor()) {
46920             //this.getRawValue.defer(100,this); 
46921             return this.value;
46922             return;
46923         }
46924         
46925         
46926         
46927         var value=this.getEditor().GetData();
46928         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46929         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46930          
46931     },
46932     
46933     setSize : function(w,h) {
46934         
46935         
46936         
46937         //if (this.frame && this.frame.dom.style.display == 'none') {
46938         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46939         //    return;
46940         //}
46941         //if(!this.el || !this.getEditor()) {
46942         //    this.setSize.defer(100,this, [w,h]); 
46943         //    return;
46944         //}
46945         
46946         
46947         
46948         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46949         
46950         this.frame.dom.setAttribute('width', w);
46951         this.frame.dom.setAttribute('height', h);
46952         this.frame.setSize(w,h);
46953         
46954     },
46955     
46956     toggleSourceEdit : function(value) {
46957         
46958       
46959          
46960         this.el.dom.style.display = value ? '' : 'none';
46961         this.frame.dom.style.display = value ?  'none' : '';
46962         
46963     },
46964     
46965     
46966     focus: function(tag)
46967     {
46968         if (this.frame.dom.style.display == 'none') {
46969             return Roo.form.FCKeditor.superclass.focus.call(this);
46970         }
46971         if(!this.el || !this.getEditor()) {
46972             this.focus.defer(100,this, [tag]); 
46973             return;
46974         }
46975         
46976         
46977         
46978         
46979         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46980         this.getEditor().Focus();
46981         if (tgs.length) {
46982             if (!this.getEditor().Selection.GetSelection()) {
46983                 this.focus.defer(100,this, [tag]); 
46984                 return;
46985             }
46986             
46987             
46988             var r = this.getEditor().EditorDocument.createRange();
46989             r.setStart(tgs[0],0);
46990             r.setEnd(tgs[0],0);
46991             this.getEditor().Selection.GetSelection().removeAllRanges();
46992             this.getEditor().Selection.GetSelection().addRange(r);
46993             this.getEditor().Focus();
46994         }
46995         
46996     },
46997     
46998     
46999     
47000     replaceTextarea : function()
47001     {
47002         if ( document.getElementById( this.getId() + '___Frame' ) )
47003             return ;
47004         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
47005         //{
47006             // We must check the elements firstly using the Id and then the name.
47007         var oTextarea = document.getElementById( this.getId() );
47008         
47009         var colElementsByName = document.getElementsByName( this.getId() ) ;
47010          
47011         oTextarea.style.display = 'none' ;
47012
47013         if ( oTextarea.tabIndex ) {            
47014             this.TabIndex = oTextarea.tabIndex ;
47015         }
47016         
47017         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47018         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47019         this.frame = Roo.get(this.getId() + '___Frame')
47020     },
47021     
47022     _getConfigHtml : function()
47023     {
47024         var sConfig = '' ;
47025
47026         for ( var o in this.fckconfig ) {
47027             sConfig += sConfig.length > 0  ? '&amp;' : '';
47028             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47029         }
47030
47031         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47032     },
47033     
47034     
47035     _getIFrameHtml : function()
47036     {
47037         var sFile = 'fckeditor.html' ;
47038         /* no idea what this is about..
47039         try
47040         {
47041             if ( (/fcksource=true/i).test( window.top.location.search ) )
47042                 sFile = 'fckeditor.original.html' ;
47043         }
47044         catch (e) { 
47045         */
47046
47047         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47048         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47049         
47050         
47051         var html = '<iframe id="' + this.getId() +
47052             '___Frame" src="' + sLink +
47053             '" width="' + this.width +
47054             '" height="' + this.height + '"' +
47055             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47056             ' frameborder="0" scrolling="no"></iframe>' ;
47057
47058         return html ;
47059     },
47060     
47061     _insertHtmlBefore : function( html, element )
47062     {
47063         if ( element.insertAdjacentHTML )       {
47064             // IE
47065             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47066         } else { // Gecko
47067             var oRange = document.createRange() ;
47068             oRange.setStartBefore( element ) ;
47069             var oFragment = oRange.createContextualFragment( html );
47070             element.parentNode.insertBefore( oFragment, element ) ;
47071         }
47072     }
47073     
47074     
47075   
47076     
47077     
47078     
47079     
47080
47081 });
47082
47083 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47084
47085 function FCKeditor_OnComplete(editorInstance){
47086     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47087     f.fckEditor = editorInstance;
47088     //console.log("loaded");
47089     f.fireEvent('editorinit', f, editorInstance);
47090
47091   
47092
47093  
47094
47095
47096
47097
47098
47099
47100
47101
47102
47103
47104
47105
47106
47107
47108
47109 //<script type="text/javascript">
47110 /**
47111  * @class Roo.form.GridField
47112  * @extends Roo.form.Field
47113  * Embed a grid (or editable grid into a form)
47114  * STATUS ALPHA
47115  * 
47116  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47117  * it needs 
47118  * xgrid.store = Roo.data.Store
47119  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47120  * xgrid.store.reader = Roo.data.JsonReader 
47121  * 
47122  * 
47123  * @constructor
47124  * Creates a new GridField
47125  * @param {Object} config Configuration options
47126  */
47127 Roo.form.GridField = function(config){
47128     Roo.form.GridField.superclass.constructor.call(this, config);
47129      
47130 };
47131
47132 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47133     /**
47134      * @cfg {Number} width  - used to restrict width of grid..
47135      */
47136     width : 100,
47137     /**
47138      * @cfg {Number} height - used to restrict height of grid..
47139      */
47140     height : 50,
47141      /**
47142      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47143          * 
47144          *}
47145      */
47146     xgrid : false, 
47147     /**
47148      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47149      * {tag: "input", type: "checkbox", autocomplete: "off"})
47150      */
47151    // defaultAutoCreate : { tag: 'div' },
47152     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47153     /**
47154      * @cfg {String} addTitle Text to include for adding a title.
47155      */
47156     addTitle : false,
47157     //
47158     onResize : function(){
47159         Roo.form.Field.superclass.onResize.apply(this, arguments);
47160     },
47161
47162     initEvents : function(){
47163         // Roo.form.Checkbox.superclass.initEvents.call(this);
47164         // has no events...
47165        
47166     },
47167
47168
47169     getResizeEl : function(){
47170         return this.wrap;
47171     },
47172
47173     getPositionEl : function(){
47174         return this.wrap;
47175     },
47176
47177     // private
47178     onRender : function(ct, position){
47179         
47180         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47181         var style = this.style;
47182         delete this.style;
47183         
47184         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47185         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47186         this.viewEl = this.wrap.createChild({ tag: 'div' });
47187         if (style) {
47188             this.viewEl.applyStyles(style);
47189         }
47190         if (this.width) {
47191             this.viewEl.setWidth(this.width);
47192         }
47193         if (this.height) {
47194             this.viewEl.setHeight(this.height);
47195         }
47196         //if(this.inputValue !== undefined){
47197         //this.setValue(this.value);
47198         
47199         
47200         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47201         
47202         
47203         this.grid.render();
47204         this.grid.getDataSource().on('remove', this.refreshValue, this);
47205         this.grid.getDataSource().on('update', this.refreshValue, this);
47206         this.grid.on('afteredit', this.refreshValue, this);
47207  
47208     },
47209      
47210     
47211     /**
47212      * Sets the value of the item. 
47213      * @param {String} either an object  or a string..
47214      */
47215     setValue : function(v){
47216         //this.value = v;
47217         v = v || []; // empty set..
47218         // this does not seem smart - it really only affects memoryproxy grids..
47219         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47220             var ds = this.grid.getDataSource();
47221             // assumes a json reader..
47222             var data = {}
47223             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47224             ds.loadData( data);
47225         }
47226         // clear selection so it does not get stale.
47227         if (this.grid.sm) { 
47228             this.grid.sm.clearSelections();
47229         }
47230         
47231         Roo.form.GridField.superclass.setValue.call(this, v);
47232         this.refreshValue();
47233         // should load data in the grid really....
47234     },
47235     
47236     // private
47237     refreshValue: function() {
47238          var val = [];
47239         this.grid.getDataSource().each(function(r) {
47240             val.push(r.data);
47241         });
47242         this.el.dom.value = Roo.encode(val);
47243     }
47244     
47245      
47246     
47247     
47248 });/*
47249  * Based on:
47250  * Ext JS Library 1.1.1
47251  * Copyright(c) 2006-2007, Ext JS, LLC.
47252  *
47253  * Originally Released Under LGPL - original licence link has changed is not relivant.
47254  *
47255  * Fork - LGPL
47256  * <script type="text/javascript">
47257  */
47258 /**
47259  * @class Roo.form.DisplayField
47260  * @extends Roo.form.Field
47261  * A generic Field to display non-editable data.
47262  * @constructor
47263  * Creates a new Display Field item.
47264  * @param {Object} config Configuration options
47265  */
47266 Roo.form.DisplayField = function(config){
47267     Roo.form.DisplayField.superclass.constructor.call(this, config);
47268     
47269 };
47270
47271 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47272     inputType:      'hidden',
47273     allowBlank:     true,
47274     readOnly:         true,
47275     
47276  
47277     /**
47278      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47279      */
47280     focusClass : undefined,
47281     /**
47282      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47283      */
47284     fieldClass: 'x-form-field',
47285     
47286      /**
47287      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47288      */
47289     valueRenderer: undefined,
47290     
47291     width: 100,
47292     /**
47293      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47294      * {tag: "input", type: "checkbox", autocomplete: "off"})
47295      */
47296      
47297  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47298
47299     onResize : function(){
47300         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47301         
47302     },
47303
47304     initEvents : function(){
47305         // Roo.form.Checkbox.superclass.initEvents.call(this);
47306         // has no events...
47307        
47308     },
47309
47310
47311     getResizeEl : function(){
47312         return this.wrap;
47313     },
47314
47315     getPositionEl : function(){
47316         return this.wrap;
47317     },
47318
47319     // private
47320     onRender : function(ct, position){
47321         
47322         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47323         //if(this.inputValue !== undefined){
47324         this.wrap = this.el.wrap();
47325         
47326         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47327         
47328         if (this.bodyStyle) {
47329             this.viewEl.applyStyles(this.bodyStyle);
47330         }
47331         //this.viewEl.setStyle('padding', '2px');
47332         
47333         this.setValue(this.value);
47334         
47335     },
47336 /*
47337     // private
47338     initValue : Roo.emptyFn,
47339
47340   */
47341
47342         // private
47343     onClick : function(){
47344         
47345     },
47346
47347     /**
47348      * Sets the checked state of the checkbox.
47349      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47350      */
47351     setValue : function(v){
47352         this.value = v;
47353         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47354         // this might be called before we have a dom element..
47355         if (!this.viewEl) {
47356             return;
47357         }
47358         this.viewEl.dom.innerHTML = html;
47359         Roo.form.DisplayField.superclass.setValue.call(this, v);
47360
47361     }
47362 });/*
47363  * 
47364  * Licence- LGPL
47365  * 
47366  */
47367
47368 /**
47369  * @class Roo.form.DayPicker
47370  * @extends Roo.form.Field
47371  * A Day picker show [M] [T] [W] ....
47372  * @constructor
47373  * Creates a new Day Picker
47374  * @param {Object} config Configuration options
47375  */
47376 Roo.form.DayPicker= function(config){
47377     Roo.form.DayPicker.superclass.constructor.call(this, config);
47378      
47379 };
47380
47381 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47382     /**
47383      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47384      */
47385     focusClass : undefined,
47386     /**
47387      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47388      */
47389     fieldClass: "x-form-field",
47390    
47391     /**
47392      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47393      * {tag: "input", type: "checkbox", autocomplete: "off"})
47394      */
47395     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47396     
47397    
47398     actionMode : 'viewEl', 
47399     //
47400     // private
47401  
47402     inputType : 'hidden',
47403     
47404      
47405     inputElement: false, // real input element?
47406     basedOn: false, // ????
47407     
47408     isFormField: true, // not sure where this is needed!!!!
47409
47410     onResize : function(){
47411         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47412         if(!this.boxLabel){
47413             this.el.alignTo(this.wrap, 'c-c');
47414         }
47415     },
47416
47417     initEvents : function(){
47418         Roo.form.Checkbox.superclass.initEvents.call(this);
47419         this.el.on("click", this.onClick,  this);
47420         this.el.on("change", this.onClick,  this);
47421     },
47422
47423
47424     getResizeEl : function(){
47425         return this.wrap;
47426     },
47427
47428     getPositionEl : function(){
47429         return this.wrap;
47430     },
47431
47432     
47433     // private
47434     onRender : function(ct, position){
47435         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47436        
47437         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47438         
47439         var r1 = '<table><tr>';
47440         var r2 = '<tr class="x-form-daypick-icons">';
47441         for (var i=0; i < 7; i++) {
47442             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47443             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47444         }
47445         
47446         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47447         viewEl.select('img').on('click', this.onClick, this);
47448         this.viewEl = viewEl;   
47449         
47450         
47451         // this will not work on Chrome!!!
47452         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47453         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47454         
47455         
47456           
47457
47458     },
47459
47460     // private
47461     initValue : Roo.emptyFn,
47462
47463     /**
47464      * Returns the checked state of the checkbox.
47465      * @return {Boolean} True if checked, else false
47466      */
47467     getValue : function(){
47468         return this.el.dom.value;
47469         
47470     },
47471
47472         // private
47473     onClick : function(e){ 
47474         //this.setChecked(!this.checked);
47475         Roo.get(e.target).toggleClass('x-menu-item-checked');
47476         this.refreshValue();
47477         //if(this.el.dom.checked != this.checked){
47478         //    this.setValue(this.el.dom.checked);
47479        // }
47480     },
47481     
47482     // private
47483     refreshValue : function()
47484     {
47485         var val = '';
47486         this.viewEl.select('img',true).each(function(e,i,n)  {
47487             val += e.is(".x-menu-item-checked") ? String(n) : '';
47488         });
47489         this.setValue(val, true);
47490     },
47491
47492     /**
47493      * Sets the checked state of the checkbox.
47494      * On is always based on a string comparison between inputValue and the param.
47495      * @param {Boolean/String} value - the value to set 
47496      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47497      */
47498     setValue : function(v,suppressEvent){
47499         if (!this.el.dom) {
47500             return;
47501         }
47502         var old = this.el.dom.value ;
47503         this.el.dom.value = v;
47504         if (suppressEvent) {
47505             return ;
47506         }
47507          
47508         // update display..
47509         this.viewEl.select('img',true).each(function(e,i,n)  {
47510             
47511             var on = e.is(".x-menu-item-checked");
47512             var newv = v.indexOf(String(n)) > -1;
47513             if (on != newv) {
47514                 e.toggleClass('x-menu-item-checked');
47515             }
47516             
47517         });
47518         
47519         
47520         this.fireEvent('change', this, v, old);
47521         
47522         
47523     },
47524    
47525     // handle setting of hidden value by some other method!!?!?
47526     setFromHidden: function()
47527     {
47528         if(!this.el){
47529             return;
47530         }
47531         //console.log("SET FROM HIDDEN");
47532         //alert('setFrom hidden');
47533         this.setValue(this.el.dom.value);
47534     },
47535     
47536     onDestroy : function()
47537     {
47538         if(this.viewEl){
47539             Roo.get(this.viewEl).remove();
47540         }
47541          
47542         Roo.form.DayPicker.superclass.onDestroy.call(this);
47543     }
47544
47545 });/*
47546  * RooJS Library 1.1.1
47547  * Copyright(c) 2008-2011  Alan Knowles
47548  *
47549  * License - LGPL
47550  */
47551  
47552
47553 /**
47554  * @class Roo.form.ComboCheck
47555  * @extends Roo.form.ComboBox
47556  * A combobox for multiple select items.
47557  *
47558  * FIXME - could do with a reset button..
47559  * 
47560  * @constructor
47561  * Create a new ComboCheck
47562  * @param {Object} config Configuration options
47563  */
47564 Roo.form.ComboCheck = function(config){
47565     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47566     // should verify some data...
47567     // like
47568     // hiddenName = required..
47569     // displayField = required
47570     // valudField == required
47571     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47572     var _t = this;
47573     Roo.each(req, function(e) {
47574         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47575             throw "Roo.form.ComboCheck : missing value for: " + e;
47576         }
47577     });
47578     
47579     
47580 };
47581
47582 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47583      
47584      
47585     editable : false,
47586      
47587     selectedClass: 'x-menu-item-checked', 
47588     
47589     // private
47590     onRender : function(ct, position){
47591         var _t = this;
47592         
47593         
47594         
47595         if(!this.tpl){
47596             var cls = 'x-combo-list';
47597
47598             
47599             this.tpl =  new Roo.Template({
47600                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47601                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47602                    '<span>{' + this.displayField + '}</span>' +
47603                     '</div>' 
47604                 
47605             });
47606         }
47607  
47608         
47609         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47610         this.view.singleSelect = false;
47611         this.view.multiSelect = true;
47612         this.view.toggleSelect = true;
47613         this.pageTb.add(new Roo.Toolbar.Fill(), {
47614             
47615             text: 'Done',
47616             handler: function()
47617             {
47618                 _t.collapse();
47619             }
47620         });
47621     },
47622     
47623     onViewOver : function(e, t){
47624         // do nothing...
47625         return;
47626         
47627     },
47628     
47629     onViewClick : function(doFocus,index){
47630         return;
47631         
47632     },
47633     select: function () {
47634         //Roo.log("SELECT CALLED");
47635     },
47636      
47637     selectByValue : function(xv, scrollIntoView){
47638         var ar = this.getValueArray();
47639         var sels = [];
47640         
47641         Roo.each(ar, function(v) {
47642             if(v === undefined || v === null){
47643                 return;
47644             }
47645             var r = this.findRecord(this.valueField, v);
47646             if(r){
47647                 sels.push(this.store.indexOf(r))
47648                 
47649             }
47650         },this);
47651         this.view.select(sels);
47652         return false;
47653     },
47654     
47655     
47656     
47657     onSelect : function(record, index){
47658        // Roo.log("onselect Called");
47659        // this is only called by the clear button now..
47660         this.view.clearSelections();
47661         this.setValue('[]');
47662         if (this.value != this.valueBefore) {
47663             this.fireEvent('change', this, this.value, this.valueBefore);
47664             this.valueBefore = this.value;
47665         }
47666     },
47667     getValueArray : function()
47668     {
47669         var ar = [] ;
47670         
47671         try {
47672             //Roo.log(this.value);
47673             if (typeof(this.value) == 'undefined') {
47674                 return [];
47675             }
47676             var ar = Roo.decode(this.value);
47677             return  ar instanceof Array ? ar : []; //?? valid?
47678             
47679         } catch(e) {
47680             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47681             return [];
47682         }
47683          
47684     },
47685     expand : function ()
47686     {
47687         
47688         Roo.form.ComboCheck.superclass.expand.call(this);
47689         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47690         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47691         
47692
47693     },
47694     
47695     collapse : function(){
47696         Roo.form.ComboCheck.superclass.collapse.call(this);
47697         var sl = this.view.getSelectedIndexes();
47698         var st = this.store;
47699         var nv = [];
47700         var tv = [];
47701         var r;
47702         Roo.each(sl, function(i) {
47703             r = st.getAt(i);
47704             nv.push(r.get(this.valueField));
47705         },this);
47706         this.setValue(Roo.encode(nv));
47707         if (this.value != this.valueBefore) {
47708
47709             this.fireEvent('change', this, this.value, this.valueBefore);
47710             this.valueBefore = this.value;
47711         }
47712         
47713     },
47714     
47715     setValue : function(v){
47716         // Roo.log(v);
47717         this.value = v;
47718         
47719         var vals = this.getValueArray();
47720         var tv = [];
47721         Roo.each(vals, function(k) {
47722             var r = this.findRecord(this.valueField, k);
47723             if(r){
47724                 tv.push(r.data[this.displayField]);
47725             }else if(this.valueNotFoundText !== undefined){
47726                 tv.push( this.valueNotFoundText );
47727             }
47728         },this);
47729        // Roo.log(tv);
47730         
47731         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47732         this.hiddenField.value = v;
47733         this.value = v;
47734     }
47735     
47736 });/*
47737  * Based on:
47738  * Ext JS Library 1.1.1
47739  * Copyright(c) 2006-2007, Ext JS, LLC.
47740  *
47741  * Originally Released Under LGPL - original licence link has changed is not relivant.
47742  *
47743  * Fork - LGPL
47744  * <script type="text/javascript">
47745  */
47746  
47747 /**
47748  * @class Roo.form.Signature
47749  * @extends Roo.form.Field
47750  * Signature field.  
47751  * @constructor
47752  * 
47753  * @param {Object} config Configuration options
47754  */
47755
47756 Roo.form.Signature = function(config){
47757     Roo.form.Signature.superclass.constructor.call(this, config);
47758     
47759     this.addEvents({// not in used??
47760          /**
47761          * @event confirm
47762          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47763              * @param {Roo.form.Signature} combo This combo box
47764              */
47765         'confirm' : true,
47766         /**
47767          * @event reset
47768          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47769              * @param {Roo.form.ComboBox} combo This combo box
47770              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47771              */
47772         'reset' : true
47773     });
47774 };
47775
47776 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47777     /**
47778      * @cfg {Object} labels Label to use when rendering a form.
47779      * defaults to 
47780      * labels : { 
47781      *      clear : "Clear",
47782      *      confirm : "Confirm"
47783      *  }
47784      */
47785     labels : { 
47786         clear : "Clear",
47787         confirm : "Confirm"
47788     },
47789     /**
47790      * @cfg {Number} width The signature panel width (defaults to 300)
47791      */
47792     width: 300,
47793     /**
47794      * @cfg {Number} height The signature panel height (defaults to 100)
47795      */
47796     height : 100,
47797     /**
47798      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47799      */
47800     allowBlank : false,
47801     
47802     //private
47803     // {Object} signPanel The signature SVG panel element (defaults to {})
47804     signPanel : {},
47805     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47806     isMouseDown : false,
47807     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47808     isConfirmed : false,
47809     // {String} signatureTmp SVG mapping string (defaults to empty string)
47810     signatureTmp : '',
47811     
47812     
47813     defaultAutoCreate : { // modified by initCompnoent..
47814         tag: "input",
47815         type:"hidden"
47816     },
47817
47818     // private
47819     onRender : function(ct, position){
47820         
47821         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47822         
47823         this.wrap = this.el.wrap({
47824             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47825         });
47826         
47827         this.createToolbar(this);
47828         this.signPanel = this.wrap.createChild({
47829                 tag: 'div',
47830                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47831             }, this.el
47832         );
47833             
47834         this.svgID = Roo.id();
47835         this.svgEl = this.signPanel.createChild({
47836               xmlns : 'http://www.w3.org/2000/svg',
47837               tag : 'svg',
47838               id : this.svgID + "-svg",
47839               width: this.width,
47840               height: this.height,
47841               viewBox: '0 0 '+this.width+' '+this.height,
47842               cn : [
47843                 {
47844                     tag: "rect",
47845                     id: this.svgID + "-svg-r",
47846                     width: this.width,
47847                     height: this.height,
47848                     fill: "#ffa"
47849                 },
47850                 {
47851                     tag: "line",
47852                     id: this.svgID + "-svg-l",
47853                     x1: "0", // start
47854                     y1: (this.height*0.8), // start set the line in 80% of height
47855                     x2: this.width, // end
47856                     y2: (this.height*0.8), // end set the line in 80% of height
47857                     'stroke': "#666",
47858                     'stroke-width': "1",
47859                     'stroke-dasharray': "3",
47860                     'shape-rendering': "crispEdges",
47861                     'pointer-events': "none"
47862                 },
47863                 {
47864                     tag: "path",
47865                     id: this.svgID + "-svg-p",
47866                     'stroke': "navy",
47867                     'stroke-width': "3",
47868                     'fill': "none",
47869                     'pointer-events': 'none'
47870                 }
47871               ]
47872         });
47873         this.createSVG();
47874         this.svgBox = this.svgEl.dom.getScreenCTM();
47875     },
47876     createSVG : function(){ 
47877         var svg = this.signPanel;
47878         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47879         var t = this;
47880
47881         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47882         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47883         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47884         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47885         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47886         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47887         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47888         
47889     },
47890     isTouchEvent : function(e){
47891         return e.type.match(/^touch/);
47892     },
47893     getCoords : function (e) {
47894         var pt    = this.svgEl.dom.createSVGPoint();
47895         pt.x = e.clientX; 
47896         pt.y = e.clientY;
47897         if (this.isTouchEvent(e)) {
47898             pt.x =  e.targetTouches[0].clientX 
47899             pt.y = e.targetTouches[0].clientY;
47900         }
47901         var a = this.svgEl.dom.getScreenCTM();
47902         var b = a.inverse();
47903         var mx = pt.matrixTransform(b);
47904         return mx.x + ',' + mx.y;
47905     },
47906     //mouse event headler 
47907     down : function (e) {
47908         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47909         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47910         
47911         this.isMouseDown = true;
47912         
47913         e.preventDefault();
47914     },
47915     move : function (e) {
47916         if (this.isMouseDown) {
47917             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47918             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47919         }
47920         
47921         e.preventDefault();
47922     },
47923     up : function (e) {
47924         this.isMouseDown = false;
47925         var sp = this.signatureTmp.split(' ');
47926         
47927         if(sp.length > 1){
47928             if(!sp[sp.length-2].match(/^L/)){
47929                 sp.pop();
47930                 sp.pop();
47931                 sp.push("");
47932                 this.signatureTmp = sp.join(" ");
47933             }
47934         }
47935         if(this.getValue() != this.signatureTmp){
47936             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47937             this.isConfirmed = false;
47938         }
47939         e.preventDefault();
47940     },
47941     
47942     /**
47943      * Protected method that will not generally be called directly. It
47944      * is called when the editor creates its toolbar. Override this method if you need to
47945      * add custom toolbar buttons.
47946      * @param {HtmlEditor} editor
47947      */
47948     createToolbar : function(editor){
47949          function btn(id, toggle, handler){
47950             var xid = fid + '-'+ id ;
47951             return {
47952                 id : xid,
47953                 cmd : id,
47954                 cls : 'x-btn-icon x-edit-'+id,
47955                 enableToggle:toggle !== false,
47956                 scope: editor, // was editor...
47957                 handler:handler||editor.relayBtnCmd,
47958                 clickEvent:'mousedown',
47959                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47960                 tabIndex:-1
47961             };
47962         }
47963         
47964         
47965         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47966         this.tb = tb;
47967         this.tb.add(
47968            {
47969                 cls : ' x-signature-btn x-signature-'+id,
47970                 scope: editor, // was editor...
47971                 handler: this.reset,
47972                 clickEvent:'mousedown',
47973                 text: this.labels.clear
47974             },
47975             {
47976                  xtype : 'Fill',
47977                  xns: Roo.Toolbar
47978             }, 
47979             {
47980                 cls : '  x-signature-btn x-signature-'+id,
47981                 scope: editor, // was editor...
47982                 handler: this.confirmHandler,
47983                 clickEvent:'mousedown',
47984                 text: this.labels.confirm
47985             }
47986         );
47987     
47988     },
47989     //public
47990     /**
47991      * when user is clicked confirm then show this image.....
47992      * 
47993      * @return {String} Image Data URI
47994      */
47995     getImageDataURI : function(){
47996         var svg = this.svgEl.dom.parentNode.innerHTML;
47997         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47998         return src; 
47999     },
48000     /**
48001      * 
48002      * @return {Boolean} this.isConfirmed
48003      */
48004     getConfirmed : function(){
48005         return this.isConfirmed;
48006     },
48007     /**
48008      * 
48009      * @return {Number} this.width
48010      */
48011     getWidth : function(){
48012         return this.width;
48013     },
48014     /**
48015      * 
48016      * @return {Number} this.height
48017      */
48018     getHeight : function(){
48019         return this.height;
48020     },
48021     // private
48022     getSignature : function(){
48023         return this.signatureTmp;
48024     },
48025     // private
48026     reset : function(){
48027         this.signatureTmp = '';
48028         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48029         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48030         this.isConfirmed = false;
48031         Roo.form.Signature.superclass.reset.call(this);
48032     },
48033     setSignature : function(s){
48034         this.signatureTmp = s;
48035         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48036         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48037         this.setValue(s);
48038         this.isConfirmed = false;
48039         Roo.form.Signature.superclass.reset.call(this);
48040     }, 
48041     test : function(){
48042 //        Roo.log(this.signPanel.dom.contentWindow.up())
48043     },
48044     //private
48045     setConfirmed : function(){
48046         
48047         
48048         
48049 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48050     },
48051     // private
48052     confirmHandler : function(){
48053         if(!this.getSignature()){
48054             return;
48055         }
48056         
48057         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48058         this.setValue(this.getSignature());
48059         this.isConfirmed = true;
48060         
48061         this.fireEvent('confirm', this);
48062     },
48063     // private
48064     // Subclasses should provide the validation implementation by overriding this
48065     validateValue : function(value){
48066         if(this.allowBlank){
48067             return true;
48068         }
48069         
48070         if(this.isConfirmed){
48071             return true;
48072         }
48073         return false;
48074     }
48075 });/*
48076  * Based on:
48077  * Ext JS Library 1.1.1
48078  * Copyright(c) 2006-2007, Ext JS, LLC.
48079  *
48080  * Originally Released Under LGPL - original licence link has changed is not relivant.
48081  *
48082  * Fork - LGPL
48083  * <script type="text/javascript">
48084  */
48085  
48086
48087 /**
48088  * @class Roo.form.ComboBox
48089  * @extends Roo.form.TriggerField
48090  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48091  * @constructor
48092  * Create a new ComboBox.
48093  * @param {Object} config Configuration options
48094  */
48095 Roo.form.Select = function(config){
48096     Roo.form.Select.superclass.constructor.call(this, config);
48097      
48098 };
48099
48100 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48101     /**
48102      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48103      */
48104     /**
48105      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48106      * rendering into an Roo.Editor, defaults to false)
48107      */
48108     /**
48109      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48110      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48111      */
48112     /**
48113      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48114      */
48115     /**
48116      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48117      * the dropdown list (defaults to undefined, with no header element)
48118      */
48119
48120      /**
48121      * @cfg {String/Roo.Template} tpl The template to use to render the output
48122      */
48123      
48124     // private
48125     defaultAutoCreate : {tag: "select"  },
48126     /**
48127      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48128      */
48129     listWidth: undefined,
48130     /**
48131      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48132      * mode = 'remote' or 'text' if mode = 'local')
48133      */
48134     displayField: undefined,
48135     /**
48136      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48137      * mode = 'remote' or 'value' if mode = 'local'). 
48138      * Note: use of a valueField requires the user make a selection
48139      * in order for a value to be mapped.
48140      */
48141     valueField: undefined,
48142     
48143     
48144     /**
48145      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48146      * field's data value (defaults to the underlying DOM element's name)
48147      */
48148     hiddenName: undefined,
48149     /**
48150      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48151      */
48152     listClass: '',
48153     /**
48154      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48155      */
48156     selectedClass: 'x-combo-selected',
48157     /**
48158      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48159      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48160      * which displays a downward arrow icon).
48161      */
48162     triggerClass : 'x-form-arrow-trigger',
48163     /**
48164      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48165      */
48166     shadow:'sides',
48167     /**
48168      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48169      * anchor positions (defaults to 'tl-bl')
48170      */
48171     listAlign: 'tl-bl?',
48172     /**
48173      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48174      */
48175     maxHeight: 300,
48176     /**
48177      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48178      * query specified by the allQuery config option (defaults to 'query')
48179      */
48180     triggerAction: 'query',
48181     /**
48182      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48183      * (defaults to 4, does not apply if editable = false)
48184      */
48185     minChars : 4,
48186     /**
48187      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48188      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48189      */
48190     typeAhead: false,
48191     /**
48192      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48193      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48194      */
48195     queryDelay: 500,
48196     /**
48197      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48198      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48199      */
48200     pageSize: 0,
48201     /**
48202      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48203      * when editable = true (defaults to false)
48204      */
48205     selectOnFocus:false,
48206     /**
48207      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48208      */
48209     queryParam: 'query',
48210     /**
48211      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48212      * when mode = 'remote' (defaults to 'Loading...')
48213      */
48214     loadingText: 'Loading...',
48215     /**
48216      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48217      */
48218     resizable: false,
48219     /**
48220      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48221      */
48222     handleHeight : 8,
48223     /**
48224      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48225      * traditional select (defaults to true)
48226      */
48227     editable: true,
48228     /**
48229      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48230      */
48231     allQuery: '',
48232     /**
48233      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48234      */
48235     mode: 'remote',
48236     /**
48237      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48238      * listWidth has a higher value)
48239      */
48240     minListWidth : 70,
48241     /**
48242      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48243      * allow the user to set arbitrary text into the field (defaults to false)
48244      */
48245     forceSelection:false,
48246     /**
48247      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48248      * if typeAhead = true (defaults to 250)
48249      */
48250     typeAheadDelay : 250,
48251     /**
48252      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48253      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48254      */
48255     valueNotFoundText : undefined,
48256     
48257     /**
48258      * @cfg {String} defaultValue The value displayed after loading the store.
48259      */
48260     defaultValue: '',
48261     
48262     /**
48263      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48264      */
48265     blockFocus : false,
48266     
48267     /**
48268      * @cfg {Boolean} disableClear Disable showing of clear button.
48269      */
48270     disableClear : false,
48271     /**
48272      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48273      */
48274     alwaysQuery : false,
48275     
48276     //private
48277     addicon : false,
48278     editicon: false,
48279     
48280     // element that contains real text value.. (when hidden is used..)
48281      
48282     // private
48283     onRender : function(ct, position){
48284         Roo.form.Field.prototype.onRender.call(this, ct, position);
48285         
48286         if(this.store){
48287             this.store.on('beforeload', this.onBeforeLoad, this);
48288             this.store.on('load', this.onLoad, this);
48289             this.store.on('loadexception', this.onLoadException, this);
48290             this.store.load({});
48291         }
48292         
48293         
48294         
48295     },
48296
48297     // private
48298     initEvents : function(){
48299         //Roo.form.ComboBox.superclass.initEvents.call(this);
48300  
48301     },
48302
48303     onDestroy : function(){
48304        
48305         if(this.store){
48306             this.store.un('beforeload', this.onBeforeLoad, this);
48307             this.store.un('load', this.onLoad, this);
48308             this.store.un('loadexception', this.onLoadException, this);
48309         }
48310         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48311     },
48312
48313     // private
48314     fireKey : function(e){
48315         if(e.isNavKeyPress() && !this.list.isVisible()){
48316             this.fireEvent("specialkey", this, e);
48317         }
48318     },
48319
48320     // private
48321     onResize: function(w, h){
48322         
48323         return; 
48324     
48325         
48326     },
48327
48328     /**
48329      * Allow or prevent the user from directly editing the field text.  If false is passed,
48330      * the user will only be able to select from the items defined in the dropdown list.  This method
48331      * is the runtime equivalent of setting the 'editable' config option at config time.
48332      * @param {Boolean} value True to allow the user to directly edit the field text
48333      */
48334     setEditable : function(value){
48335          
48336     },
48337
48338     // private
48339     onBeforeLoad : function(){
48340         
48341         Roo.log("Select before load");
48342         return;
48343     
48344         this.innerList.update(this.loadingText ?
48345                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48346         //this.restrictHeight();
48347         this.selectedIndex = -1;
48348     },
48349
48350     // private
48351     onLoad : function(){
48352
48353     
48354         var dom = this.el.dom;
48355         dom.innerHTML = '';
48356          var od = dom.ownerDocument;
48357          
48358         if (this.emptyText) {
48359             var op = od.createElement('option');
48360             op.setAttribute('value', '');
48361             op.innerHTML = String.format('{0}', this.emptyText);
48362             dom.appendChild(op);
48363         }
48364         if(this.store.getCount() > 0){
48365            
48366             var vf = this.valueField;
48367             var df = this.displayField;
48368             this.store.data.each(function(r) {
48369                 // which colmsn to use... testing - cdoe / title..
48370                 var op = od.createElement('option');
48371                 op.setAttribute('value', r.data[vf]);
48372                 op.innerHTML = String.format('{0}', r.data[df]);
48373                 dom.appendChild(op);
48374             });
48375             if (typeof(this.defaultValue != 'undefined')) {
48376                 this.setValue(this.defaultValue);
48377             }
48378             
48379              
48380         }else{
48381             //this.onEmptyResults();
48382         }
48383         //this.el.focus();
48384     },
48385     // private
48386     onLoadException : function()
48387     {
48388         dom.innerHTML = '';
48389             
48390         Roo.log("Select on load exception");
48391         return;
48392     
48393         this.collapse();
48394         Roo.log(this.store.reader.jsonData);
48395         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48396             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48397         }
48398         
48399         
48400     },
48401     // private
48402     onTypeAhead : function(){
48403          
48404     },
48405
48406     // private
48407     onSelect : function(record, index){
48408         Roo.log('on select?');
48409         return;
48410         if(this.fireEvent('beforeselect', this, record, index) !== false){
48411             this.setFromData(index > -1 ? record.data : false);
48412             this.collapse();
48413             this.fireEvent('select', this, record, index);
48414         }
48415     },
48416
48417     /**
48418      * Returns the currently selected field value or empty string if no value is set.
48419      * @return {String} value The selected value
48420      */
48421     getValue : function(){
48422         var dom = this.el.dom;
48423         this.value = dom.options[dom.selectedIndex].value;
48424         return this.value;
48425         
48426     },
48427
48428     /**
48429      * Clears any text/value currently set in the field
48430      */
48431     clearValue : function(){
48432         this.value = '';
48433         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48434         
48435     },
48436
48437     /**
48438      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48439      * will be displayed in the field.  If the value does not match the data value of an existing item,
48440      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48441      * Otherwise the field will be blank (although the value will still be set).
48442      * @param {String} value The value to match
48443      */
48444     setValue : function(v){
48445         var d = this.el.dom;
48446         for (var i =0; i < d.options.length;i++) {
48447             if (v == d.options[i].value) {
48448                 d.selectedIndex = i;
48449                 this.value = v;
48450                 return;
48451             }
48452         }
48453         this.clearValue();
48454     },
48455     /**
48456      * @property {Object} the last set data for the element
48457      */
48458     
48459     lastData : false,
48460     /**
48461      * Sets the value of the field based on a object which is related to the record format for the store.
48462      * @param {Object} value the value to set as. or false on reset?
48463      */
48464     setFromData : function(o){
48465         Roo.log('setfrom data?');
48466          
48467         
48468         
48469     },
48470     // private
48471     reset : function(){
48472         this.clearValue();
48473     },
48474     // private
48475     findRecord : function(prop, value){
48476         
48477         return false;
48478     
48479         var record;
48480         if(this.store.getCount() > 0){
48481             this.store.each(function(r){
48482                 if(r.data[prop] == value){
48483                     record = r;
48484                     return false;
48485                 }
48486                 return true;
48487             });
48488         }
48489         return record;
48490     },
48491     
48492     getName: function()
48493     {
48494         // returns hidden if it's set..
48495         if (!this.rendered) {return ''};
48496         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48497         
48498     },
48499      
48500
48501     
48502
48503     // private
48504     onEmptyResults : function(){
48505         Roo.log('empty results');
48506         //this.collapse();
48507     },
48508
48509     /**
48510      * Returns true if the dropdown list is expanded, else false.
48511      */
48512     isExpanded : function(){
48513         return false;
48514     },
48515
48516     /**
48517      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48518      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48519      * @param {String} value The data value of the item to select
48520      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48521      * selected item if it is not currently in view (defaults to true)
48522      * @return {Boolean} True if the value matched an item in the list, else false
48523      */
48524     selectByValue : function(v, scrollIntoView){
48525         Roo.log('select By Value');
48526         return false;
48527     
48528         if(v !== undefined && v !== null){
48529             var r = this.findRecord(this.valueField || this.displayField, v);
48530             if(r){
48531                 this.select(this.store.indexOf(r), scrollIntoView);
48532                 return true;
48533             }
48534         }
48535         return false;
48536     },
48537
48538     /**
48539      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48540      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48541      * @param {Number} index The zero-based index of the list item to select
48542      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48543      * selected item if it is not currently in view (defaults to true)
48544      */
48545     select : function(index, scrollIntoView){
48546         Roo.log('select ');
48547         return  ;
48548         
48549         this.selectedIndex = index;
48550         this.view.select(index);
48551         if(scrollIntoView !== false){
48552             var el = this.view.getNode(index);
48553             if(el){
48554                 this.innerList.scrollChildIntoView(el, false);
48555             }
48556         }
48557     },
48558
48559       
48560
48561     // private
48562     validateBlur : function(){
48563         
48564         return;
48565         
48566     },
48567
48568     // private
48569     initQuery : function(){
48570         this.doQuery(this.getRawValue());
48571     },
48572
48573     // private
48574     doForce : function(){
48575         if(this.el.dom.value.length > 0){
48576             this.el.dom.value =
48577                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48578              
48579         }
48580     },
48581
48582     /**
48583      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48584      * query allowing the query action to be canceled if needed.
48585      * @param {String} query The SQL query to execute
48586      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48587      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48588      * saved in the current store (defaults to false)
48589      */
48590     doQuery : function(q, forceAll){
48591         
48592         Roo.log('doQuery?');
48593         if(q === undefined || q === null){
48594             q = '';
48595         }
48596         var qe = {
48597             query: q,
48598             forceAll: forceAll,
48599             combo: this,
48600             cancel:false
48601         };
48602         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48603             return false;
48604         }
48605         q = qe.query;
48606         forceAll = qe.forceAll;
48607         if(forceAll === true || (q.length >= this.minChars)){
48608             if(this.lastQuery != q || this.alwaysQuery){
48609                 this.lastQuery = q;
48610                 if(this.mode == 'local'){
48611                     this.selectedIndex = -1;
48612                     if(forceAll){
48613                         this.store.clearFilter();
48614                     }else{
48615                         this.store.filter(this.displayField, q);
48616                     }
48617                     this.onLoad();
48618                 }else{
48619                     this.store.baseParams[this.queryParam] = q;
48620                     this.store.load({
48621                         params: this.getParams(q)
48622                     });
48623                     this.expand();
48624                 }
48625             }else{
48626                 this.selectedIndex = -1;
48627                 this.onLoad();   
48628             }
48629         }
48630     },
48631
48632     // private
48633     getParams : function(q){
48634         var p = {};
48635         //p[this.queryParam] = q;
48636         if(this.pageSize){
48637             p.start = 0;
48638             p.limit = this.pageSize;
48639         }
48640         return p;
48641     },
48642
48643     /**
48644      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48645      */
48646     collapse : function(){
48647         
48648     },
48649
48650     // private
48651     collapseIf : function(e){
48652         
48653     },
48654
48655     /**
48656      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48657      */
48658     expand : function(){
48659         
48660     } ,
48661
48662     // private
48663      
48664
48665     /** 
48666     * @cfg {Boolean} grow 
48667     * @hide 
48668     */
48669     /** 
48670     * @cfg {Number} growMin 
48671     * @hide 
48672     */
48673     /** 
48674     * @cfg {Number} growMax 
48675     * @hide 
48676     */
48677     /**
48678      * @hide
48679      * @method autoSize
48680      */
48681     
48682     setWidth : function()
48683     {
48684         
48685     },
48686     getResizeEl : function(){
48687         return this.el;
48688     }
48689 });//<script type="text/javasscript">
48690  
48691
48692 /**
48693  * @class Roo.DDView
48694  * A DnD enabled version of Roo.View.
48695  * @param {Element/String} container The Element in which to create the View.
48696  * @param {String} tpl The template string used to create the markup for each element of the View
48697  * @param {Object} config The configuration properties. These include all the config options of
48698  * {@link Roo.View} plus some specific to this class.<br>
48699  * <p>
48700  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48701  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48702  * <p>
48703  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48704 .x-view-drag-insert-above {
48705         border-top:1px dotted #3366cc;
48706 }
48707 .x-view-drag-insert-below {
48708         border-bottom:1px dotted #3366cc;
48709 }
48710 </code></pre>
48711  * 
48712  */
48713  
48714 Roo.DDView = function(container, tpl, config) {
48715     Roo.DDView.superclass.constructor.apply(this, arguments);
48716     this.getEl().setStyle("outline", "0px none");
48717     this.getEl().unselectable();
48718     if (this.dragGroup) {
48719                 this.setDraggable(this.dragGroup.split(","));
48720     }
48721     if (this.dropGroup) {
48722                 this.setDroppable(this.dropGroup.split(","));
48723     }
48724     if (this.deletable) {
48725         this.setDeletable();
48726     }
48727     this.isDirtyFlag = false;
48728         this.addEvents({
48729                 "drop" : true
48730         });
48731 };
48732
48733 Roo.extend(Roo.DDView, Roo.View, {
48734 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48735 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48736 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48737 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48738
48739         isFormField: true,
48740
48741         reset: Roo.emptyFn,
48742         
48743         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48744
48745         validate: function() {
48746                 return true;
48747         },
48748         
48749         destroy: function() {
48750                 this.purgeListeners();
48751                 this.getEl.removeAllListeners();
48752                 this.getEl().remove();
48753                 if (this.dragZone) {
48754                         if (this.dragZone.destroy) {
48755                                 this.dragZone.destroy();
48756                         }
48757                 }
48758                 if (this.dropZone) {
48759                         if (this.dropZone.destroy) {
48760                                 this.dropZone.destroy();
48761                         }
48762                 }
48763         },
48764
48765 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48766         getName: function() {
48767                 return this.name;
48768         },
48769
48770 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48771         setValue: function(v) {
48772                 if (!this.store) {
48773                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48774                 }
48775                 var data = {};
48776                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48777                 this.store.proxy = new Roo.data.MemoryProxy(data);
48778                 this.store.load();
48779         },
48780
48781 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48782         getValue: function() {
48783                 var result = '(';
48784                 this.store.each(function(rec) {
48785                         result += rec.id + ',';
48786                 });
48787                 return result.substr(0, result.length - 1) + ')';
48788         },
48789         
48790         getIds: function() {
48791                 var i = 0, result = new Array(this.store.getCount());
48792                 this.store.each(function(rec) {
48793                         result[i++] = rec.id;
48794                 });
48795                 return result;
48796         },
48797         
48798         isDirty: function() {
48799                 return this.isDirtyFlag;
48800         },
48801
48802 /**
48803  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48804  *      whole Element becomes the target, and this causes the drop gesture to append.
48805  */
48806     getTargetFromEvent : function(e) {
48807                 var target = e.getTarget();
48808                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48809                 target = target.parentNode;
48810                 }
48811                 if (!target) {
48812                         target = this.el.dom.lastChild || this.el.dom;
48813                 }
48814                 return target;
48815     },
48816
48817 /**
48818  *      Create the drag data which consists of an object which has the property "ddel" as
48819  *      the drag proxy element. 
48820  */
48821     getDragData : function(e) {
48822         var target = this.findItemFromChild(e.getTarget());
48823                 if(target) {
48824                         this.handleSelection(e);
48825                         var selNodes = this.getSelectedNodes();
48826             var dragData = {
48827                 source: this,
48828                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48829                 nodes: selNodes,
48830                 records: []
48831                         };
48832                         var selectedIndices = this.getSelectedIndexes();
48833                         for (var i = 0; i < selectedIndices.length; i++) {
48834                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48835                         }
48836                         if (selNodes.length == 1) {
48837                                 dragData.ddel = target.cloneNode(true); // the div element
48838                         } else {
48839                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48840                                 div.className = 'multi-proxy';
48841                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48842                                         div.appendChild(selNodes[i].cloneNode(true));
48843                                 }
48844                                 dragData.ddel = div;
48845                         }
48846             //console.log(dragData)
48847             //console.log(dragData.ddel.innerHTML)
48848                         return dragData;
48849                 }
48850         //console.log('nodragData')
48851                 return false;
48852     },
48853     
48854 /**     Specify to which ddGroup items in this DDView may be dragged. */
48855     setDraggable: function(ddGroup) {
48856         if (ddGroup instanceof Array) {
48857                 Roo.each(ddGroup, this.setDraggable, this);
48858                 return;
48859         }
48860         if (this.dragZone) {
48861                 this.dragZone.addToGroup(ddGroup);
48862         } else {
48863                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48864                                 containerScroll: true,
48865                                 ddGroup: ddGroup 
48866
48867                         });
48868 //                      Draggability implies selection. DragZone's mousedown selects the element.
48869                         if (!this.multiSelect) { this.singleSelect = true; }
48870
48871 //                      Wire the DragZone's handlers up to methods in *this*
48872                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48873                 }
48874     },
48875
48876 /**     Specify from which ddGroup this DDView accepts drops. */
48877     setDroppable: function(ddGroup) {
48878         if (ddGroup instanceof Array) {
48879                 Roo.each(ddGroup, this.setDroppable, this);
48880                 return;
48881         }
48882         if (this.dropZone) {
48883                 this.dropZone.addToGroup(ddGroup);
48884         } else {
48885                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48886                                 containerScroll: true,
48887                                 ddGroup: ddGroup
48888                         });
48889
48890 //                      Wire the DropZone's handlers up to methods in *this*
48891                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48892                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48893                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48894                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48895                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48896                 }
48897     },
48898
48899 /**     Decide whether to drop above or below a View node. */
48900     getDropPoint : function(e, n, dd){
48901         if (n == this.el.dom) { return "above"; }
48902                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48903                 var c = t + (b - t) / 2;
48904                 var y = Roo.lib.Event.getPageY(e);
48905                 if(y <= c) {
48906                         return "above";
48907                 }else{
48908                         return "below";
48909                 }
48910     },
48911
48912     onNodeEnter : function(n, dd, e, data){
48913                 return false;
48914     },
48915     
48916     onNodeOver : function(n, dd, e, data){
48917                 var pt = this.getDropPoint(e, n, dd);
48918                 // set the insert point style on the target node
48919                 var dragElClass = this.dropNotAllowed;
48920                 if (pt) {
48921                         var targetElClass;
48922                         if (pt == "above"){
48923                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48924                                 targetElClass = "x-view-drag-insert-above";
48925                         } else {
48926                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48927                                 targetElClass = "x-view-drag-insert-below";
48928                         }
48929                         if (this.lastInsertClass != targetElClass){
48930                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48931                                 this.lastInsertClass = targetElClass;
48932                         }
48933                 }
48934                 return dragElClass;
48935         },
48936
48937     onNodeOut : function(n, dd, e, data){
48938                 this.removeDropIndicators(n);
48939     },
48940
48941     onNodeDrop : function(n, dd, e, data){
48942         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48943                 return false;
48944         }
48945         var pt = this.getDropPoint(e, n, dd);
48946                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48947                 if (pt == "below") { insertAt++; }
48948                 for (var i = 0; i < data.records.length; i++) {
48949                         var r = data.records[i];
48950                         var dup = this.store.getById(r.id);
48951                         if (dup && (dd != this.dragZone)) {
48952                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48953                         } else {
48954                                 if (data.copy) {
48955                                         this.store.insert(insertAt++, r.copy());
48956                                 } else {
48957                                         data.source.isDirtyFlag = true;
48958                                         r.store.remove(r);
48959                                         this.store.insert(insertAt++, r);
48960                                 }
48961                                 this.isDirtyFlag = true;
48962                         }
48963                 }
48964                 this.dragZone.cachedTarget = null;
48965                 return true;
48966     },
48967
48968     removeDropIndicators : function(n){
48969                 if(n){
48970                         Roo.fly(n).removeClass([
48971                                 "x-view-drag-insert-above",
48972                                 "x-view-drag-insert-below"]);
48973                         this.lastInsertClass = "_noclass";
48974                 }
48975     },
48976
48977 /**
48978  *      Utility method. Add a delete option to the DDView's context menu.
48979  *      @param {String} imageUrl The URL of the "delete" icon image.
48980  */
48981         setDeletable: function(imageUrl) {
48982                 if (!this.singleSelect && !this.multiSelect) {
48983                         this.singleSelect = true;
48984                 }
48985                 var c = this.getContextMenu();
48986                 this.contextMenu.on("itemclick", function(item) {
48987                         switch (item.id) {
48988                                 case "delete":
48989                                         this.remove(this.getSelectedIndexes());
48990                                         break;
48991                         }
48992                 }, this);
48993                 this.contextMenu.add({
48994                         icon: imageUrl,
48995                         id: "delete",
48996                         text: 'Delete'
48997                 });
48998         },
48999         
49000 /**     Return the context menu for this DDView. */
49001         getContextMenu: function() {
49002                 if (!this.contextMenu) {
49003 //                      Create the View's context menu
49004                         this.contextMenu = new Roo.menu.Menu({
49005                                 id: this.id + "-contextmenu"
49006                         });
49007                         this.el.on("contextmenu", this.showContextMenu, this);
49008                 }
49009                 return this.contextMenu;
49010         },
49011         
49012         disableContextMenu: function() {
49013                 if (this.contextMenu) {
49014                         this.el.un("contextmenu", this.showContextMenu, this);
49015                 }
49016         },
49017
49018         showContextMenu: function(e, item) {
49019         item = this.findItemFromChild(e.getTarget());
49020                 if (item) {
49021                         e.stopEvent();
49022                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49023                         this.contextMenu.showAt(e.getXY());
49024             }
49025     },
49026
49027 /**
49028  *      Remove {@link Roo.data.Record}s at the specified indices.
49029  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49030  */
49031     remove: function(selectedIndices) {
49032                 selectedIndices = [].concat(selectedIndices);
49033                 for (var i = 0; i < selectedIndices.length; i++) {
49034                         var rec = this.store.getAt(selectedIndices[i]);
49035                         this.store.remove(rec);
49036                 }
49037     },
49038
49039 /**
49040  *      Double click fires the event, but also, if this is draggable, and there is only one other
49041  *      related DropZone, it transfers the selected node.
49042  */
49043     onDblClick : function(e){
49044         var item = this.findItemFromChild(e.getTarget());
49045         if(item){
49046             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49047                 return false;
49048             }
49049             if (this.dragGroup) {
49050                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49051                     while (targets.indexOf(this.dropZone) > -1) {
49052                             targets.remove(this.dropZone);
49053                                 }
49054                     if (targets.length == 1) {
49055                                         this.dragZone.cachedTarget = null;
49056                         var el = Roo.get(targets[0].getEl());
49057                         var box = el.getBox(true);
49058                         targets[0].onNodeDrop(el.dom, {
49059                                 target: el.dom,
49060                                 xy: [box.x, box.y + box.height - 1]
49061                         }, null, this.getDragData(e));
49062                     }
49063                 }
49064         }
49065     },
49066     
49067     handleSelection: function(e) {
49068                 this.dragZone.cachedTarget = null;
49069         var item = this.findItemFromChild(e.getTarget());
49070         if (!item) {
49071                 this.clearSelections(true);
49072                 return;
49073         }
49074                 if (item && (this.multiSelect || this.singleSelect)){
49075                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49076                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49077                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49078                                 this.unselect(item);
49079                         } else {
49080                                 this.select(item, this.multiSelect && e.ctrlKey);
49081                                 this.lastSelection = item;
49082                         }
49083                 }
49084     },
49085
49086     onItemClick : function(item, index, e){
49087                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49088                         return false;
49089                 }
49090                 return true;
49091     },
49092
49093     unselect : function(nodeInfo, suppressEvent){
49094                 var node = this.getNode(nodeInfo);
49095                 if(node && this.isSelected(node)){
49096                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49097                                 Roo.fly(node).removeClass(this.selectedClass);
49098                                 this.selections.remove(node);
49099                                 if(!suppressEvent){
49100                                         this.fireEvent("selectionchange", this, this.selections);
49101                                 }
49102                         }
49103                 }
49104     }
49105 });
49106 /*
49107  * Based on:
49108  * Ext JS Library 1.1.1
49109  * Copyright(c) 2006-2007, Ext JS, LLC.
49110  *
49111  * Originally Released Under LGPL - original licence link has changed is not relivant.
49112  *
49113  * Fork - LGPL
49114  * <script type="text/javascript">
49115  */
49116  
49117 /**
49118  * @class Roo.LayoutManager
49119  * @extends Roo.util.Observable
49120  * Base class for layout managers.
49121  */
49122 Roo.LayoutManager = function(container, config){
49123     Roo.LayoutManager.superclass.constructor.call(this);
49124     this.el = Roo.get(container);
49125     // ie scrollbar fix
49126     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49127         document.body.scroll = "no";
49128     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49129         this.el.position('relative');
49130     }
49131     this.id = this.el.id;
49132     this.el.addClass("x-layout-container");
49133     /** false to disable window resize monitoring @type Boolean */
49134     this.monitorWindowResize = true;
49135     this.regions = {};
49136     this.addEvents({
49137         /**
49138          * @event layout
49139          * Fires when a layout is performed. 
49140          * @param {Roo.LayoutManager} this
49141          */
49142         "layout" : true,
49143         /**
49144          * @event regionresized
49145          * Fires when the user resizes a region. 
49146          * @param {Roo.LayoutRegion} region The resized region
49147          * @param {Number} newSize The new size (width for east/west, height for north/south)
49148          */
49149         "regionresized" : true,
49150         /**
49151          * @event regioncollapsed
49152          * Fires when a region is collapsed. 
49153          * @param {Roo.LayoutRegion} region The collapsed region
49154          */
49155         "regioncollapsed" : true,
49156         /**
49157          * @event regionexpanded
49158          * Fires when a region is expanded.  
49159          * @param {Roo.LayoutRegion} region The expanded region
49160          */
49161         "regionexpanded" : true
49162     });
49163     this.updating = false;
49164     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49165 };
49166
49167 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49168     /**
49169      * Returns true if this layout is currently being updated
49170      * @return {Boolean}
49171      */
49172     isUpdating : function(){
49173         return this.updating; 
49174     },
49175     
49176     /**
49177      * Suspend the LayoutManager from doing auto-layouts while
49178      * making multiple add or remove calls
49179      */
49180     beginUpdate : function(){
49181         this.updating = true;    
49182     },
49183     
49184     /**
49185      * Restore auto-layouts and optionally disable the manager from performing a layout
49186      * @param {Boolean} noLayout true to disable a layout update 
49187      */
49188     endUpdate : function(noLayout){
49189         this.updating = false;
49190         if(!noLayout){
49191             this.layout();
49192         }    
49193     },
49194     
49195     layout: function(){
49196         
49197     },
49198     
49199     onRegionResized : function(region, newSize){
49200         this.fireEvent("regionresized", region, newSize);
49201         this.layout();
49202     },
49203     
49204     onRegionCollapsed : function(region){
49205         this.fireEvent("regioncollapsed", region);
49206     },
49207     
49208     onRegionExpanded : function(region){
49209         this.fireEvent("regionexpanded", region);
49210     },
49211         
49212     /**
49213      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49214      * performs box-model adjustments.
49215      * @return {Object} The size as an object {width: (the width), height: (the height)}
49216      */
49217     getViewSize : function(){
49218         var size;
49219         if(this.el.dom != document.body){
49220             size = this.el.getSize();
49221         }else{
49222             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49223         }
49224         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49225         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49226         return size;
49227     },
49228     
49229     /**
49230      * Returns the Element this layout is bound to.
49231      * @return {Roo.Element}
49232      */
49233     getEl : function(){
49234         return this.el;
49235     },
49236     
49237     /**
49238      * Returns the specified region.
49239      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49240      * @return {Roo.LayoutRegion}
49241      */
49242     getRegion : function(target){
49243         return this.regions[target.toLowerCase()];
49244     },
49245     
49246     onWindowResize : function(){
49247         if(this.monitorWindowResize){
49248             this.layout();
49249         }
49250     }
49251 });/*
49252  * Based on:
49253  * Ext JS Library 1.1.1
49254  * Copyright(c) 2006-2007, Ext JS, LLC.
49255  *
49256  * Originally Released Under LGPL - original licence link has changed is not relivant.
49257  *
49258  * Fork - LGPL
49259  * <script type="text/javascript">
49260  */
49261 /**
49262  * @class Roo.BorderLayout
49263  * @extends Roo.LayoutManager
49264  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49265  * please see: <br><br>
49266  * <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>
49267  * <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>
49268  * Example:
49269  <pre><code>
49270  var layout = new Roo.BorderLayout(document.body, {
49271     north: {
49272         initialSize: 25,
49273         titlebar: false
49274     },
49275     west: {
49276         split:true,
49277         initialSize: 200,
49278         minSize: 175,
49279         maxSize: 400,
49280         titlebar: true,
49281         collapsible: true
49282     },
49283     east: {
49284         split:true,
49285         initialSize: 202,
49286         minSize: 175,
49287         maxSize: 400,
49288         titlebar: true,
49289         collapsible: true
49290     },
49291     south: {
49292         split:true,
49293         initialSize: 100,
49294         minSize: 100,
49295         maxSize: 200,
49296         titlebar: true,
49297         collapsible: true
49298     },
49299     center: {
49300         titlebar: true,
49301         autoScroll:true,
49302         resizeTabs: true,
49303         minTabWidth: 50,
49304         preferredTabWidth: 150
49305     }
49306 });
49307
49308 // shorthand
49309 var CP = Roo.ContentPanel;
49310
49311 layout.beginUpdate();
49312 layout.add("north", new CP("north", "North"));
49313 layout.add("south", new CP("south", {title: "South", closable: true}));
49314 layout.add("west", new CP("west", {title: "West"}));
49315 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49316 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49317 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49318 layout.getRegion("center").showPanel("center1");
49319 layout.endUpdate();
49320 </code></pre>
49321
49322 <b>The container the layout is rendered into can be either the body element or any other element.
49323 If it is not the body element, the container needs to either be an absolute positioned element,
49324 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49325 the container size if it is not the body element.</b>
49326
49327 * @constructor
49328 * Create a new BorderLayout
49329 * @param {String/HTMLElement/Element} container The container this layout is bound to
49330 * @param {Object} config Configuration options
49331  */
49332 Roo.BorderLayout = function(container, config){
49333     config = config || {};
49334     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49335     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49336     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49337         var target = this.factory.validRegions[i];
49338         if(config[target]){
49339             this.addRegion(target, config[target]);
49340         }
49341     }
49342 };
49343
49344 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49345     /**
49346      * Creates and adds a new region if it doesn't already exist.
49347      * @param {String} target The target region key (north, south, east, west or center).
49348      * @param {Object} config The regions config object
49349      * @return {BorderLayoutRegion} The new region
49350      */
49351     addRegion : function(target, config){
49352         if(!this.regions[target]){
49353             var r = this.factory.create(target, this, config);
49354             this.bindRegion(target, r);
49355         }
49356         return this.regions[target];
49357     },
49358
49359     // private (kinda)
49360     bindRegion : function(name, r){
49361         this.regions[name] = r;
49362         r.on("visibilitychange", this.layout, this);
49363         r.on("paneladded", this.layout, this);
49364         r.on("panelremoved", this.layout, this);
49365         r.on("invalidated", this.layout, this);
49366         r.on("resized", this.onRegionResized, this);
49367         r.on("collapsed", this.onRegionCollapsed, this);
49368         r.on("expanded", this.onRegionExpanded, this);
49369     },
49370
49371     /**
49372      * Performs a layout update.
49373      */
49374     layout : function(){
49375         if(this.updating) return;
49376         var size = this.getViewSize();
49377         var w = size.width;
49378         var h = size.height;
49379         var centerW = w;
49380         var centerH = h;
49381         var centerY = 0;
49382         var centerX = 0;
49383         //var x = 0, y = 0;
49384
49385         var rs = this.regions;
49386         var north = rs["north"];
49387         var south = rs["south"]; 
49388         var west = rs["west"];
49389         var east = rs["east"];
49390         var center = rs["center"];
49391         //if(this.hideOnLayout){ // not supported anymore
49392             //c.el.setStyle("display", "none");
49393         //}
49394         if(north && north.isVisible()){
49395             var b = north.getBox();
49396             var m = north.getMargins();
49397             b.width = w - (m.left+m.right);
49398             b.x = m.left;
49399             b.y = m.top;
49400             centerY = b.height + b.y + m.bottom;
49401             centerH -= centerY;
49402             north.updateBox(this.safeBox(b));
49403         }
49404         if(south && south.isVisible()){
49405             var b = south.getBox();
49406             var m = south.getMargins();
49407             b.width = w - (m.left+m.right);
49408             b.x = m.left;
49409             var totalHeight = (b.height + m.top + m.bottom);
49410             b.y = h - totalHeight + m.top;
49411             centerH -= totalHeight;
49412             south.updateBox(this.safeBox(b));
49413         }
49414         if(west && west.isVisible()){
49415             var b = west.getBox();
49416             var m = west.getMargins();
49417             b.height = centerH - (m.top+m.bottom);
49418             b.x = m.left;
49419             b.y = centerY + m.top;
49420             var totalWidth = (b.width + m.left + m.right);
49421             centerX += totalWidth;
49422             centerW -= totalWidth;
49423             west.updateBox(this.safeBox(b));
49424         }
49425         if(east && east.isVisible()){
49426             var b = east.getBox();
49427             var m = east.getMargins();
49428             b.height = centerH - (m.top+m.bottom);
49429             var totalWidth = (b.width + m.left + m.right);
49430             b.x = w - totalWidth + m.left;
49431             b.y = centerY + m.top;
49432             centerW -= totalWidth;
49433             east.updateBox(this.safeBox(b));
49434         }
49435         if(center){
49436             var m = center.getMargins();
49437             var centerBox = {
49438                 x: centerX + m.left,
49439                 y: centerY + m.top,
49440                 width: centerW - (m.left+m.right),
49441                 height: centerH - (m.top+m.bottom)
49442             };
49443             //if(this.hideOnLayout){
49444                 //center.el.setStyle("display", "block");
49445             //}
49446             center.updateBox(this.safeBox(centerBox));
49447         }
49448         this.el.repaint();
49449         this.fireEvent("layout", this);
49450     },
49451
49452     // private
49453     safeBox : function(box){
49454         box.width = Math.max(0, box.width);
49455         box.height = Math.max(0, box.height);
49456         return box;
49457     },
49458
49459     /**
49460      * Adds a ContentPanel (or subclass) to this layout.
49461      * @param {String} target The target region key (north, south, east, west or center).
49462      * @param {Roo.ContentPanel} panel The panel to add
49463      * @return {Roo.ContentPanel} The added panel
49464      */
49465     add : function(target, panel){
49466          
49467         target = target.toLowerCase();
49468         return this.regions[target].add(panel);
49469     },
49470
49471     /**
49472      * Remove a ContentPanel (or subclass) to this layout.
49473      * @param {String} target The target region key (north, south, east, west or center).
49474      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49475      * @return {Roo.ContentPanel} The removed panel
49476      */
49477     remove : function(target, panel){
49478         target = target.toLowerCase();
49479         return this.regions[target].remove(panel);
49480     },
49481
49482     /**
49483      * Searches all regions for a panel with the specified id
49484      * @param {String} panelId
49485      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49486      */
49487     findPanel : function(panelId){
49488         var rs = this.regions;
49489         for(var target in rs){
49490             if(typeof rs[target] != "function"){
49491                 var p = rs[target].getPanel(panelId);
49492                 if(p){
49493                     return p;
49494                 }
49495             }
49496         }
49497         return null;
49498     },
49499
49500     /**
49501      * Searches all regions for a panel with the specified id and activates (shows) it.
49502      * @param {String/ContentPanel} panelId The panels id or the panel itself
49503      * @return {Roo.ContentPanel} The shown panel or null
49504      */
49505     showPanel : function(panelId) {
49506       var rs = this.regions;
49507       for(var target in rs){
49508          var r = rs[target];
49509          if(typeof r != "function"){
49510             if(r.hasPanel(panelId)){
49511                return r.showPanel(panelId);
49512             }
49513          }
49514       }
49515       return null;
49516    },
49517
49518    /**
49519      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49520      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49521      */
49522     restoreState : function(provider){
49523         if(!provider){
49524             provider = Roo.state.Manager;
49525         }
49526         var sm = new Roo.LayoutStateManager();
49527         sm.init(this, provider);
49528     },
49529
49530     /**
49531      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49532      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49533      * a valid ContentPanel config object.  Example:
49534      * <pre><code>
49535 // Create the main layout
49536 var layout = new Roo.BorderLayout('main-ct', {
49537     west: {
49538         split:true,
49539         minSize: 175,
49540         titlebar: true
49541     },
49542     center: {
49543         title:'Components'
49544     }
49545 }, 'main-ct');
49546
49547 // Create and add multiple ContentPanels at once via configs
49548 layout.batchAdd({
49549    west: {
49550        id: 'source-files',
49551        autoCreate:true,
49552        title:'Ext Source Files',
49553        autoScroll:true,
49554        fitToFrame:true
49555    },
49556    center : {
49557        el: cview,
49558        autoScroll:true,
49559        fitToFrame:true,
49560        toolbar: tb,
49561        resizeEl:'cbody'
49562    }
49563 });
49564 </code></pre>
49565      * @param {Object} regions An object containing ContentPanel configs by region name
49566      */
49567     batchAdd : function(regions){
49568         this.beginUpdate();
49569         for(var rname in regions){
49570             var lr = this.regions[rname];
49571             if(lr){
49572                 this.addTypedPanels(lr, regions[rname]);
49573             }
49574         }
49575         this.endUpdate();
49576     },
49577
49578     // private
49579     addTypedPanels : function(lr, ps){
49580         if(typeof ps == 'string'){
49581             lr.add(new Roo.ContentPanel(ps));
49582         }
49583         else if(ps instanceof Array){
49584             for(var i =0, len = ps.length; i < len; i++){
49585                 this.addTypedPanels(lr, ps[i]);
49586             }
49587         }
49588         else if(!ps.events){ // raw config?
49589             var el = ps.el;
49590             delete ps.el; // prevent conflict
49591             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49592         }
49593         else {  // panel object assumed!
49594             lr.add(ps);
49595         }
49596     },
49597     /**
49598      * Adds a xtype elements to the layout.
49599      * <pre><code>
49600
49601 layout.addxtype({
49602        xtype : 'ContentPanel',
49603        region: 'west',
49604        items: [ .... ]
49605    }
49606 );
49607
49608 layout.addxtype({
49609         xtype : 'NestedLayoutPanel',
49610         region: 'west',
49611         layout: {
49612            center: { },
49613            west: { }   
49614         },
49615         items : [ ... list of content panels or nested layout panels.. ]
49616    }
49617 );
49618 </code></pre>
49619      * @param {Object} cfg Xtype definition of item to add.
49620      */
49621     addxtype : function(cfg)
49622     {
49623         // basically accepts a pannel...
49624         // can accept a layout region..!?!?
49625         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49626         
49627         if (!cfg.xtype.match(/Panel$/)) {
49628             return false;
49629         }
49630         var ret = false;
49631         
49632         if (typeof(cfg.region) == 'undefined') {
49633             Roo.log("Failed to add Panel, region was not set");
49634             Roo.log(cfg);
49635             return false;
49636         }
49637         var region = cfg.region;
49638         delete cfg.region;
49639         
49640           
49641         var xitems = [];
49642         if (cfg.items) {
49643             xitems = cfg.items;
49644             delete cfg.items;
49645         }
49646         var nb = false;
49647         
49648         switch(cfg.xtype) 
49649         {
49650             case 'ContentPanel':  // ContentPanel (el, cfg)
49651             case 'ScrollPanel':  // ContentPanel (el, cfg)
49652             case 'ViewPanel': 
49653                 if(cfg.autoCreate) {
49654                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49655                 } else {
49656                     var el = this.el.createChild();
49657                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49658                 }
49659                 
49660                 this.add(region, ret);
49661                 break;
49662             
49663             
49664             case 'TreePanel': // our new panel!
49665                 cfg.el = this.el.createChild();
49666                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49667                 this.add(region, ret);
49668                 break;
49669             
49670             case 'NestedLayoutPanel': 
49671                 // create a new Layout (which is  a Border Layout...
49672                 var el = this.el.createChild();
49673                 var clayout = cfg.layout;
49674                 delete cfg.layout;
49675                 clayout.items   = clayout.items  || [];
49676                 // replace this exitems with the clayout ones..
49677                 xitems = clayout.items;
49678                  
49679                 
49680                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49681                     cfg.background = false;
49682                 }
49683                 var layout = new Roo.BorderLayout(el, clayout);
49684                 
49685                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49686                 //console.log('adding nested layout panel '  + cfg.toSource());
49687                 this.add(region, ret);
49688                 nb = {}; /// find first...
49689                 break;
49690                 
49691             case 'GridPanel': 
49692             
49693                 // needs grid and region
49694                 
49695                 //var el = this.getRegion(region).el.createChild();
49696                 var el = this.el.createChild();
49697                 // create the grid first...
49698                 
49699                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49700                 delete cfg.grid;
49701                 if (region == 'center' && this.active ) {
49702                     cfg.background = false;
49703                 }
49704                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49705                 
49706                 this.add(region, ret);
49707                 if (cfg.background) {
49708                     ret.on('activate', function(gp) {
49709                         if (!gp.grid.rendered) {
49710                             gp.grid.render();
49711                         }
49712                     });
49713                 } else {
49714                     grid.render();
49715                 }
49716                 break;
49717            
49718            
49719            
49720                 
49721                 
49722                 
49723             default:
49724                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49725                     
49726                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49727                     this.add(region, ret);
49728                 } else {
49729                 
49730                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49731                     return null;
49732                 }
49733                 
49734              // GridPanel (grid, cfg)
49735             
49736         }
49737         this.beginUpdate();
49738         // add children..
49739         var region = '';
49740         var abn = {};
49741         Roo.each(xitems, function(i)  {
49742             region = nb && i.region ? i.region : false;
49743             
49744             var add = ret.addxtype(i);
49745            
49746             if (region) {
49747                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49748                 if (!i.background) {
49749                     abn[region] = nb[region] ;
49750                 }
49751             }
49752             
49753         });
49754         this.endUpdate();
49755
49756         // make the last non-background panel active..
49757         //if (nb) { Roo.log(abn); }
49758         if (nb) {
49759             
49760             for(var r in abn) {
49761                 region = this.getRegion(r);
49762                 if (region) {
49763                     // tried using nb[r], but it does not work..
49764                      
49765                     region.showPanel(abn[r]);
49766                    
49767                 }
49768             }
49769         }
49770         return ret;
49771         
49772     }
49773 });
49774
49775 /**
49776  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49777  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49778  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49779  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49780  * <pre><code>
49781 // shorthand
49782 var CP = Roo.ContentPanel;
49783
49784 var layout = Roo.BorderLayout.create({
49785     north: {
49786         initialSize: 25,
49787         titlebar: false,
49788         panels: [new CP("north", "North")]
49789     },
49790     west: {
49791         split:true,
49792         initialSize: 200,
49793         minSize: 175,
49794         maxSize: 400,
49795         titlebar: true,
49796         collapsible: true,
49797         panels: [new CP("west", {title: "West"})]
49798     },
49799     east: {
49800         split:true,
49801         initialSize: 202,
49802         minSize: 175,
49803         maxSize: 400,
49804         titlebar: true,
49805         collapsible: true,
49806         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49807     },
49808     south: {
49809         split:true,
49810         initialSize: 100,
49811         minSize: 100,
49812         maxSize: 200,
49813         titlebar: true,
49814         collapsible: true,
49815         panels: [new CP("south", {title: "South", closable: true})]
49816     },
49817     center: {
49818         titlebar: true,
49819         autoScroll:true,
49820         resizeTabs: true,
49821         minTabWidth: 50,
49822         preferredTabWidth: 150,
49823         panels: [
49824             new CP("center1", {title: "Close Me", closable: true}),
49825             new CP("center2", {title: "Center Panel", closable: false})
49826         ]
49827     }
49828 }, document.body);
49829
49830 layout.getRegion("center").showPanel("center1");
49831 </code></pre>
49832  * @param config
49833  * @param targetEl
49834  */
49835 Roo.BorderLayout.create = function(config, targetEl){
49836     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49837     layout.beginUpdate();
49838     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49839     for(var j = 0, jlen = regions.length; j < jlen; j++){
49840         var lr = regions[j];
49841         if(layout.regions[lr] && config[lr].panels){
49842             var r = layout.regions[lr];
49843             var ps = config[lr].panels;
49844             layout.addTypedPanels(r, ps);
49845         }
49846     }
49847     layout.endUpdate();
49848     return layout;
49849 };
49850
49851 // private
49852 Roo.BorderLayout.RegionFactory = {
49853     // private
49854     validRegions : ["north","south","east","west","center"],
49855
49856     // private
49857     create : function(target, mgr, config){
49858         target = target.toLowerCase();
49859         if(config.lightweight || config.basic){
49860             return new Roo.BasicLayoutRegion(mgr, config, target);
49861         }
49862         switch(target){
49863             case "north":
49864                 return new Roo.NorthLayoutRegion(mgr, config);
49865             case "south":
49866                 return new Roo.SouthLayoutRegion(mgr, config);
49867             case "east":
49868                 return new Roo.EastLayoutRegion(mgr, config);
49869             case "west":
49870                 return new Roo.WestLayoutRegion(mgr, config);
49871             case "center":
49872                 return new Roo.CenterLayoutRegion(mgr, config);
49873         }
49874         throw 'Layout region "'+target+'" not supported.';
49875     }
49876 };/*
49877  * Based on:
49878  * Ext JS Library 1.1.1
49879  * Copyright(c) 2006-2007, Ext JS, LLC.
49880  *
49881  * Originally Released Under LGPL - original licence link has changed is not relivant.
49882  *
49883  * Fork - LGPL
49884  * <script type="text/javascript">
49885  */
49886  
49887 /**
49888  * @class Roo.BasicLayoutRegion
49889  * @extends Roo.util.Observable
49890  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49891  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49892  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49893  */
49894 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49895     this.mgr = mgr;
49896     this.position  = pos;
49897     this.events = {
49898         /**
49899          * @scope Roo.BasicLayoutRegion
49900          */
49901         
49902         /**
49903          * @event beforeremove
49904          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49905          * @param {Roo.LayoutRegion} this
49906          * @param {Roo.ContentPanel} panel The panel
49907          * @param {Object} e The cancel event object
49908          */
49909         "beforeremove" : true,
49910         /**
49911          * @event invalidated
49912          * Fires when the layout for this region is changed.
49913          * @param {Roo.LayoutRegion} this
49914          */
49915         "invalidated" : true,
49916         /**
49917          * @event visibilitychange
49918          * Fires when this region is shown or hidden 
49919          * @param {Roo.LayoutRegion} this
49920          * @param {Boolean} visibility true or false
49921          */
49922         "visibilitychange" : true,
49923         /**
49924          * @event paneladded
49925          * Fires when a panel is added. 
49926          * @param {Roo.LayoutRegion} this
49927          * @param {Roo.ContentPanel} panel The panel
49928          */
49929         "paneladded" : true,
49930         /**
49931          * @event panelremoved
49932          * Fires when a panel is removed. 
49933          * @param {Roo.LayoutRegion} this
49934          * @param {Roo.ContentPanel} panel The panel
49935          */
49936         "panelremoved" : true,
49937         /**
49938          * @event collapsed
49939          * Fires when this region is collapsed.
49940          * @param {Roo.LayoutRegion} this
49941          */
49942         "collapsed" : true,
49943         /**
49944          * @event expanded
49945          * Fires when this region is expanded.
49946          * @param {Roo.LayoutRegion} this
49947          */
49948         "expanded" : true,
49949         /**
49950          * @event slideshow
49951          * Fires when this region is slid into view.
49952          * @param {Roo.LayoutRegion} this
49953          */
49954         "slideshow" : true,
49955         /**
49956          * @event slidehide
49957          * Fires when this region slides out of view. 
49958          * @param {Roo.LayoutRegion} this
49959          */
49960         "slidehide" : true,
49961         /**
49962          * @event panelactivated
49963          * Fires when a panel is activated. 
49964          * @param {Roo.LayoutRegion} this
49965          * @param {Roo.ContentPanel} panel The activated panel
49966          */
49967         "panelactivated" : true,
49968         /**
49969          * @event resized
49970          * Fires when the user resizes this region. 
49971          * @param {Roo.LayoutRegion} this
49972          * @param {Number} newSize The new size (width for east/west, height for north/south)
49973          */
49974         "resized" : true
49975     };
49976     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49977     this.panels = new Roo.util.MixedCollection();
49978     this.panels.getKey = this.getPanelId.createDelegate(this);
49979     this.box = null;
49980     this.activePanel = null;
49981     // ensure listeners are added...
49982     
49983     if (config.listeners || config.events) {
49984         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49985             listeners : config.listeners || {},
49986             events : config.events || {}
49987         });
49988     }
49989     
49990     if(skipConfig !== true){
49991         this.applyConfig(config);
49992     }
49993 };
49994
49995 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49996     getPanelId : function(p){
49997         return p.getId();
49998     },
49999     
50000     applyConfig : function(config){
50001         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50002         this.config = config;
50003         
50004     },
50005     
50006     /**
50007      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
50008      * the width, for horizontal (north, south) the height.
50009      * @param {Number} newSize The new width or height
50010      */
50011     resizeTo : function(newSize){
50012         var el = this.el ? this.el :
50013                  (this.activePanel ? this.activePanel.getEl() : null);
50014         if(el){
50015             switch(this.position){
50016                 case "east":
50017                 case "west":
50018                     el.setWidth(newSize);
50019                     this.fireEvent("resized", this, newSize);
50020                 break;
50021                 case "north":
50022                 case "south":
50023                     el.setHeight(newSize);
50024                     this.fireEvent("resized", this, newSize);
50025                 break;                
50026             }
50027         }
50028     },
50029     
50030     getBox : function(){
50031         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50032     },
50033     
50034     getMargins : function(){
50035         return this.margins;
50036     },
50037     
50038     updateBox : function(box){
50039         this.box = box;
50040         var el = this.activePanel.getEl();
50041         el.dom.style.left = box.x + "px";
50042         el.dom.style.top = box.y + "px";
50043         this.activePanel.setSize(box.width, box.height);
50044     },
50045     
50046     /**
50047      * Returns the container element for this region.
50048      * @return {Roo.Element}
50049      */
50050     getEl : function(){
50051         return this.activePanel;
50052     },
50053     
50054     /**
50055      * Returns true if this region is currently visible.
50056      * @return {Boolean}
50057      */
50058     isVisible : function(){
50059         return this.activePanel ? true : false;
50060     },
50061     
50062     setActivePanel : function(panel){
50063         panel = this.getPanel(panel);
50064         if(this.activePanel && this.activePanel != panel){
50065             this.activePanel.setActiveState(false);
50066             this.activePanel.getEl().setLeftTop(-10000,-10000);
50067         }
50068         this.activePanel = panel;
50069         panel.setActiveState(true);
50070         if(this.box){
50071             panel.setSize(this.box.width, this.box.height);
50072         }
50073         this.fireEvent("panelactivated", this, panel);
50074         this.fireEvent("invalidated");
50075     },
50076     
50077     /**
50078      * Show the specified panel.
50079      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50080      * @return {Roo.ContentPanel} The shown panel or null
50081      */
50082     showPanel : function(panel){
50083         if(panel = this.getPanel(panel)){
50084             this.setActivePanel(panel);
50085         }
50086         return panel;
50087     },
50088     
50089     /**
50090      * Get the active panel for this region.
50091      * @return {Roo.ContentPanel} The active panel or null
50092      */
50093     getActivePanel : function(){
50094         return this.activePanel;
50095     },
50096     
50097     /**
50098      * Add the passed ContentPanel(s)
50099      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50100      * @return {Roo.ContentPanel} The panel added (if only one was added)
50101      */
50102     add : function(panel){
50103         if(arguments.length > 1){
50104             for(var i = 0, len = arguments.length; i < len; i++) {
50105                 this.add(arguments[i]);
50106             }
50107             return null;
50108         }
50109         if(this.hasPanel(panel)){
50110             this.showPanel(panel);
50111             return panel;
50112         }
50113         var el = panel.getEl();
50114         if(el.dom.parentNode != this.mgr.el.dom){
50115             this.mgr.el.dom.appendChild(el.dom);
50116         }
50117         if(panel.setRegion){
50118             panel.setRegion(this);
50119         }
50120         this.panels.add(panel);
50121         el.setStyle("position", "absolute");
50122         if(!panel.background){
50123             this.setActivePanel(panel);
50124             if(this.config.initialSize && this.panels.getCount()==1){
50125                 this.resizeTo(this.config.initialSize);
50126             }
50127         }
50128         this.fireEvent("paneladded", this, panel);
50129         return panel;
50130     },
50131     
50132     /**
50133      * Returns true if the panel is in this region.
50134      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50135      * @return {Boolean}
50136      */
50137     hasPanel : function(panel){
50138         if(typeof panel == "object"){ // must be panel obj
50139             panel = panel.getId();
50140         }
50141         return this.getPanel(panel) ? true : false;
50142     },
50143     
50144     /**
50145      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50146      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50147      * @param {Boolean} preservePanel Overrides the config preservePanel option
50148      * @return {Roo.ContentPanel} The panel that was removed
50149      */
50150     remove : function(panel, preservePanel){
50151         panel = this.getPanel(panel);
50152         if(!panel){
50153             return null;
50154         }
50155         var e = {};
50156         this.fireEvent("beforeremove", this, panel, e);
50157         if(e.cancel === true){
50158             return null;
50159         }
50160         var panelId = panel.getId();
50161         this.panels.removeKey(panelId);
50162         return panel;
50163     },
50164     
50165     /**
50166      * Returns the panel specified or null if it's not in this region.
50167      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50168      * @return {Roo.ContentPanel}
50169      */
50170     getPanel : function(id){
50171         if(typeof id == "object"){ // must be panel obj
50172             return id;
50173         }
50174         return this.panels.get(id);
50175     },
50176     
50177     /**
50178      * Returns this regions position (north/south/east/west/center).
50179      * @return {String} 
50180      */
50181     getPosition: function(){
50182         return this.position;    
50183     }
50184 });/*
50185  * Based on:
50186  * Ext JS Library 1.1.1
50187  * Copyright(c) 2006-2007, Ext JS, LLC.
50188  *
50189  * Originally Released Under LGPL - original licence link has changed is not relivant.
50190  *
50191  * Fork - LGPL
50192  * <script type="text/javascript">
50193  */
50194  
50195 /**
50196  * @class Roo.LayoutRegion
50197  * @extends Roo.BasicLayoutRegion
50198  * This class represents a region in a layout manager.
50199  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50200  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50201  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50202  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50203  * @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})
50204  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50205  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50206  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50207  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50208  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50209  * @cfg {String}    title           The title for the region (overrides panel titles)
50210  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50211  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50212  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50213  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50214  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50215  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50216  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50217  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50218  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50219  * @cfg {Boolean}   showPin         True to show a pin button
50220  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50221  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50222  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50223  * @cfg {Number}    width           For East/West panels
50224  * @cfg {Number}    height          For North/South panels
50225  * @cfg {Boolean}   split           To show the splitter
50226  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50227  */
50228 Roo.LayoutRegion = function(mgr, config, pos){
50229     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50230     var dh = Roo.DomHelper;
50231     /** This region's container element 
50232     * @type Roo.Element */
50233     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50234     /** This region's title element 
50235     * @type Roo.Element */
50236
50237     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50238         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50239         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50240     ]}, true);
50241     this.titleEl.enableDisplayMode();
50242     /** This region's title text element 
50243     * @type HTMLElement */
50244     this.titleTextEl = this.titleEl.dom.firstChild;
50245     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50246     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50247     this.closeBtn.enableDisplayMode();
50248     this.closeBtn.on("click", this.closeClicked, this);
50249     this.closeBtn.hide();
50250
50251     this.createBody(config);
50252     this.visible = true;
50253     this.collapsed = false;
50254
50255     if(config.hideWhenEmpty){
50256         this.hide();
50257         this.on("paneladded", this.validateVisibility, this);
50258         this.on("panelremoved", this.validateVisibility, this);
50259     }
50260     this.applyConfig(config);
50261 };
50262
50263 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50264
50265     createBody : function(){
50266         /** This region's body element 
50267         * @type Roo.Element */
50268         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50269     },
50270
50271     applyConfig : function(c){
50272         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50273             var dh = Roo.DomHelper;
50274             if(c.titlebar !== false){
50275                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50276                 this.collapseBtn.on("click", this.collapse, this);
50277                 this.collapseBtn.enableDisplayMode();
50278
50279                 if(c.showPin === true || this.showPin){
50280                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50281                     this.stickBtn.enableDisplayMode();
50282                     this.stickBtn.on("click", this.expand, this);
50283                     this.stickBtn.hide();
50284                 }
50285             }
50286             /** This region's collapsed element
50287             * @type Roo.Element */
50288             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50289                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50290             ]}, true);
50291             if(c.floatable !== false){
50292                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50293                this.collapsedEl.on("click", this.collapseClick, this);
50294             }
50295
50296             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50297                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50298                    id: "message", unselectable: "on", style:{"float":"left"}});
50299                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50300              }
50301             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50302             this.expandBtn.on("click", this.expand, this);
50303         }
50304         if(this.collapseBtn){
50305             this.collapseBtn.setVisible(c.collapsible == true);
50306         }
50307         this.cmargins = c.cmargins || this.cmargins ||
50308                          (this.position == "west" || this.position == "east" ?
50309                              {top: 0, left: 2, right:2, bottom: 0} :
50310                              {top: 2, left: 0, right:0, bottom: 2});
50311         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50312         this.bottomTabs = c.tabPosition != "top";
50313         this.autoScroll = c.autoScroll || false;
50314         if(this.autoScroll){
50315             this.bodyEl.setStyle("overflow", "auto");
50316         }else{
50317             this.bodyEl.setStyle("overflow", "hidden");
50318         }
50319         //if(c.titlebar !== false){
50320             if((!c.titlebar && !c.title) || c.titlebar === false){
50321                 this.titleEl.hide();
50322             }else{
50323                 this.titleEl.show();
50324                 if(c.title){
50325                     this.titleTextEl.innerHTML = c.title;
50326                 }
50327             }
50328         //}
50329         this.duration = c.duration || .30;
50330         this.slideDuration = c.slideDuration || .45;
50331         this.config = c;
50332         if(c.collapsed){
50333             this.collapse(true);
50334         }
50335         if(c.hidden){
50336             this.hide();
50337         }
50338     },
50339     /**
50340      * Returns true if this region is currently visible.
50341      * @return {Boolean}
50342      */
50343     isVisible : function(){
50344         return this.visible;
50345     },
50346
50347     /**
50348      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50349      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50350      */
50351     setCollapsedTitle : function(title){
50352         title = title || "&#160;";
50353         if(this.collapsedTitleTextEl){
50354             this.collapsedTitleTextEl.innerHTML = title;
50355         }
50356     },
50357
50358     getBox : function(){
50359         var b;
50360         if(!this.collapsed){
50361             b = this.el.getBox(false, true);
50362         }else{
50363             b = this.collapsedEl.getBox(false, true);
50364         }
50365         return b;
50366     },
50367
50368     getMargins : function(){
50369         return this.collapsed ? this.cmargins : this.margins;
50370     },
50371
50372     highlight : function(){
50373         this.el.addClass("x-layout-panel-dragover");
50374     },
50375
50376     unhighlight : function(){
50377         this.el.removeClass("x-layout-panel-dragover");
50378     },
50379
50380     updateBox : function(box){
50381         this.box = box;
50382         if(!this.collapsed){
50383             this.el.dom.style.left = box.x + "px";
50384             this.el.dom.style.top = box.y + "px";
50385             this.updateBody(box.width, box.height);
50386         }else{
50387             this.collapsedEl.dom.style.left = box.x + "px";
50388             this.collapsedEl.dom.style.top = box.y + "px";
50389             this.collapsedEl.setSize(box.width, box.height);
50390         }
50391         if(this.tabs){
50392             this.tabs.autoSizeTabs();
50393         }
50394     },
50395
50396     updateBody : function(w, h){
50397         if(w !== null){
50398             this.el.setWidth(w);
50399             w -= this.el.getBorderWidth("rl");
50400             if(this.config.adjustments){
50401                 w += this.config.adjustments[0];
50402             }
50403         }
50404         if(h !== null){
50405             this.el.setHeight(h);
50406             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50407             h -= this.el.getBorderWidth("tb");
50408             if(this.config.adjustments){
50409                 h += this.config.adjustments[1];
50410             }
50411             this.bodyEl.setHeight(h);
50412             if(this.tabs){
50413                 h = this.tabs.syncHeight(h);
50414             }
50415         }
50416         if(this.panelSize){
50417             w = w !== null ? w : this.panelSize.width;
50418             h = h !== null ? h : this.panelSize.height;
50419         }
50420         if(this.activePanel){
50421             var el = this.activePanel.getEl();
50422             w = w !== null ? w : el.getWidth();
50423             h = h !== null ? h : el.getHeight();
50424             this.panelSize = {width: w, height: h};
50425             this.activePanel.setSize(w, h);
50426         }
50427         if(Roo.isIE && this.tabs){
50428             this.tabs.el.repaint();
50429         }
50430     },
50431
50432     /**
50433      * Returns the container element for this region.
50434      * @return {Roo.Element}
50435      */
50436     getEl : function(){
50437         return this.el;
50438     },
50439
50440     /**
50441      * Hides this region.
50442      */
50443     hide : function(){
50444         if(!this.collapsed){
50445             this.el.dom.style.left = "-2000px";
50446             this.el.hide();
50447         }else{
50448             this.collapsedEl.dom.style.left = "-2000px";
50449             this.collapsedEl.hide();
50450         }
50451         this.visible = false;
50452         this.fireEvent("visibilitychange", this, false);
50453     },
50454
50455     /**
50456      * Shows this region if it was previously hidden.
50457      */
50458     show : function(){
50459         if(!this.collapsed){
50460             this.el.show();
50461         }else{
50462             this.collapsedEl.show();
50463         }
50464         this.visible = true;
50465         this.fireEvent("visibilitychange", this, true);
50466     },
50467
50468     closeClicked : function(){
50469         if(this.activePanel){
50470             this.remove(this.activePanel);
50471         }
50472     },
50473
50474     collapseClick : function(e){
50475         if(this.isSlid){
50476            e.stopPropagation();
50477            this.slideIn();
50478         }else{
50479            e.stopPropagation();
50480            this.slideOut();
50481         }
50482     },
50483
50484     /**
50485      * Collapses this region.
50486      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50487      */
50488     collapse : function(skipAnim){
50489         if(this.collapsed) return;
50490         this.collapsed = true;
50491         if(this.split){
50492             this.split.el.hide();
50493         }
50494         if(this.config.animate && skipAnim !== true){
50495             this.fireEvent("invalidated", this);
50496             this.animateCollapse();
50497         }else{
50498             this.el.setLocation(-20000,-20000);
50499             this.el.hide();
50500             this.collapsedEl.show();
50501             this.fireEvent("collapsed", this);
50502             this.fireEvent("invalidated", this);
50503         }
50504     },
50505
50506     animateCollapse : function(){
50507         // overridden
50508     },
50509
50510     /**
50511      * Expands this region if it was previously collapsed.
50512      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50513      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50514      */
50515     expand : function(e, skipAnim){
50516         if(e) e.stopPropagation();
50517         if(!this.collapsed || this.el.hasActiveFx()) return;
50518         if(this.isSlid){
50519             this.afterSlideIn();
50520             skipAnim = true;
50521         }
50522         this.collapsed = false;
50523         if(this.config.animate && skipAnim !== true){
50524             this.animateExpand();
50525         }else{
50526             this.el.show();
50527             if(this.split){
50528                 this.split.el.show();
50529             }
50530             this.collapsedEl.setLocation(-2000,-2000);
50531             this.collapsedEl.hide();
50532             this.fireEvent("invalidated", this);
50533             this.fireEvent("expanded", this);
50534         }
50535     },
50536
50537     animateExpand : function(){
50538         // overridden
50539     },
50540
50541     initTabs : function()
50542     {
50543         this.bodyEl.setStyle("overflow", "hidden");
50544         var ts = new Roo.TabPanel(
50545                 this.bodyEl.dom,
50546                 {
50547                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50548                     disableTooltips: this.config.disableTabTips,
50549                     toolbar : this.config.toolbar
50550                 }
50551         );
50552         if(this.config.hideTabs){
50553             ts.stripWrap.setDisplayed(false);
50554         }
50555         this.tabs = ts;
50556         ts.resizeTabs = this.config.resizeTabs === true;
50557         ts.minTabWidth = this.config.minTabWidth || 40;
50558         ts.maxTabWidth = this.config.maxTabWidth || 250;
50559         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50560         ts.monitorResize = false;
50561         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50562         ts.bodyEl.addClass('x-layout-tabs-body');
50563         this.panels.each(this.initPanelAsTab, this);
50564     },
50565
50566     initPanelAsTab : function(panel){
50567         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50568                     this.config.closeOnTab && panel.isClosable());
50569         if(panel.tabTip !== undefined){
50570             ti.setTooltip(panel.tabTip);
50571         }
50572         ti.on("activate", function(){
50573               this.setActivePanel(panel);
50574         }, this);
50575         if(this.config.closeOnTab){
50576             ti.on("beforeclose", function(t, e){
50577                 e.cancel = true;
50578                 this.remove(panel);
50579             }, this);
50580         }
50581         return ti;
50582     },
50583
50584     updatePanelTitle : function(panel, title){
50585         if(this.activePanel == panel){
50586             this.updateTitle(title);
50587         }
50588         if(this.tabs){
50589             var ti = this.tabs.getTab(panel.getEl().id);
50590             ti.setText(title);
50591             if(panel.tabTip !== undefined){
50592                 ti.setTooltip(panel.tabTip);
50593             }
50594         }
50595     },
50596
50597     updateTitle : function(title){
50598         if(this.titleTextEl && !this.config.title){
50599             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50600         }
50601     },
50602
50603     setActivePanel : function(panel){
50604         panel = this.getPanel(panel);
50605         if(this.activePanel && this.activePanel != panel){
50606             this.activePanel.setActiveState(false);
50607         }
50608         this.activePanel = panel;
50609         panel.setActiveState(true);
50610         if(this.panelSize){
50611             panel.setSize(this.panelSize.width, this.panelSize.height);
50612         }
50613         if(this.closeBtn){
50614             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50615         }
50616         this.updateTitle(panel.getTitle());
50617         if(this.tabs){
50618             this.fireEvent("invalidated", this);
50619         }
50620         this.fireEvent("panelactivated", this, panel);
50621     },
50622
50623     /**
50624      * Shows the specified panel.
50625      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50626      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50627      */
50628     showPanel : function(panel)
50629     {
50630         panel = this.getPanel(panel);
50631         if(panel){
50632             if(this.tabs){
50633                 var tab = this.tabs.getTab(panel.getEl().id);
50634                 if(tab.isHidden()){
50635                     this.tabs.unhideTab(tab.id);
50636                 }
50637                 tab.activate();
50638             }else{
50639                 this.setActivePanel(panel);
50640             }
50641         }
50642         return panel;
50643     },
50644
50645     /**
50646      * Get the active panel for this region.
50647      * @return {Roo.ContentPanel} The active panel or null
50648      */
50649     getActivePanel : function(){
50650         return this.activePanel;
50651     },
50652
50653     validateVisibility : function(){
50654         if(this.panels.getCount() < 1){
50655             this.updateTitle("&#160;");
50656             this.closeBtn.hide();
50657             this.hide();
50658         }else{
50659             if(!this.isVisible()){
50660                 this.show();
50661             }
50662         }
50663     },
50664
50665     /**
50666      * Adds the passed ContentPanel(s) to this region.
50667      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50668      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50669      */
50670     add : function(panel){
50671         if(arguments.length > 1){
50672             for(var i = 0, len = arguments.length; i < len; i++) {
50673                 this.add(arguments[i]);
50674             }
50675             return null;
50676         }
50677         if(this.hasPanel(panel)){
50678             this.showPanel(panel);
50679             return panel;
50680         }
50681         panel.setRegion(this);
50682         this.panels.add(panel);
50683         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50684             this.bodyEl.dom.appendChild(panel.getEl().dom);
50685             if(panel.background !== true){
50686                 this.setActivePanel(panel);
50687             }
50688             this.fireEvent("paneladded", this, panel);
50689             return panel;
50690         }
50691         if(!this.tabs){
50692             this.initTabs();
50693         }else{
50694             this.initPanelAsTab(panel);
50695         }
50696         if(panel.background !== true){
50697             this.tabs.activate(panel.getEl().id);
50698         }
50699         this.fireEvent("paneladded", this, panel);
50700         return panel;
50701     },
50702
50703     /**
50704      * Hides the tab for the specified panel.
50705      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50706      */
50707     hidePanel : function(panel){
50708         if(this.tabs && (panel = this.getPanel(panel))){
50709             this.tabs.hideTab(panel.getEl().id);
50710         }
50711     },
50712
50713     /**
50714      * Unhides the tab for a previously hidden panel.
50715      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50716      */
50717     unhidePanel : function(panel){
50718         if(this.tabs && (panel = this.getPanel(panel))){
50719             this.tabs.unhideTab(panel.getEl().id);
50720         }
50721     },
50722
50723     clearPanels : function(){
50724         while(this.panels.getCount() > 0){
50725              this.remove(this.panels.first());
50726         }
50727     },
50728
50729     /**
50730      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50731      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50732      * @param {Boolean} preservePanel Overrides the config preservePanel option
50733      * @return {Roo.ContentPanel} The panel that was removed
50734      */
50735     remove : function(panel, preservePanel){
50736         panel = this.getPanel(panel);
50737         if(!panel){
50738             return null;
50739         }
50740         var e = {};
50741         this.fireEvent("beforeremove", this, panel, e);
50742         if(e.cancel === true){
50743             return null;
50744         }
50745         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50746         var panelId = panel.getId();
50747         this.panels.removeKey(panelId);
50748         if(preservePanel){
50749             document.body.appendChild(panel.getEl().dom);
50750         }
50751         if(this.tabs){
50752             this.tabs.removeTab(panel.getEl().id);
50753         }else if (!preservePanel){
50754             this.bodyEl.dom.removeChild(panel.getEl().dom);
50755         }
50756         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50757             var p = this.panels.first();
50758             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50759             tempEl.appendChild(p.getEl().dom);
50760             this.bodyEl.update("");
50761             this.bodyEl.dom.appendChild(p.getEl().dom);
50762             tempEl = null;
50763             this.updateTitle(p.getTitle());
50764             this.tabs = null;
50765             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50766             this.setActivePanel(p);
50767         }
50768         panel.setRegion(null);
50769         if(this.activePanel == panel){
50770             this.activePanel = null;
50771         }
50772         if(this.config.autoDestroy !== false && preservePanel !== true){
50773             try{panel.destroy();}catch(e){}
50774         }
50775         this.fireEvent("panelremoved", this, panel);
50776         return panel;
50777     },
50778
50779     /**
50780      * Returns the TabPanel component used by this region
50781      * @return {Roo.TabPanel}
50782      */
50783     getTabs : function(){
50784         return this.tabs;
50785     },
50786
50787     createTool : function(parentEl, className){
50788         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50789             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50790         btn.addClassOnOver("x-layout-tools-button-over");
50791         return btn;
50792     }
50793 });/*
50794  * Based on:
50795  * Ext JS Library 1.1.1
50796  * Copyright(c) 2006-2007, Ext JS, LLC.
50797  *
50798  * Originally Released Under LGPL - original licence link has changed is not relivant.
50799  *
50800  * Fork - LGPL
50801  * <script type="text/javascript">
50802  */
50803  
50804
50805
50806 /**
50807  * @class Roo.SplitLayoutRegion
50808  * @extends Roo.LayoutRegion
50809  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50810  */
50811 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50812     this.cursor = cursor;
50813     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50814 };
50815
50816 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50817     splitTip : "Drag to resize.",
50818     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50819     useSplitTips : false,
50820
50821     applyConfig : function(config){
50822         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50823         if(config.split){
50824             if(!this.split){
50825                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50826                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50827                 /** The SplitBar for this region 
50828                 * @type Roo.SplitBar */
50829                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50830                 this.split.on("moved", this.onSplitMove, this);
50831                 this.split.useShim = config.useShim === true;
50832                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50833                 if(this.useSplitTips){
50834                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50835                 }
50836                 if(config.collapsible){
50837                     this.split.el.on("dblclick", this.collapse,  this);
50838                 }
50839             }
50840             if(typeof config.minSize != "undefined"){
50841                 this.split.minSize = config.minSize;
50842             }
50843             if(typeof config.maxSize != "undefined"){
50844                 this.split.maxSize = config.maxSize;
50845             }
50846             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50847                 this.hideSplitter();
50848             }
50849         }
50850     },
50851
50852     getHMaxSize : function(){
50853          var cmax = this.config.maxSize || 10000;
50854          var center = this.mgr.getRegion("center");
50855          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50856     },
50857
50858     getVMaxSize : function(){
50859          var cmax = this.config.maxSize || 10000;
50860          var center = this.mgr.getRegion("center");
50861          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50862     },
50863
50864     onSplitMove : function(split, newSize){
50865         this.fireEvent("resized", this, newSize);
50866     },
50867     
50868     /** 
50869      * Returns the {@link Roo.SplitBar} for this region.
50870      * @return {Roo.SplitBar}
50871      */
50872     getSplitBar : function(){
50873         return this.split;
50874     },
50875     
50876     hide : function(){
50877         this.hideSplitter();
50878         Roo.SplitLayoutRegion.superclass.hide.call(this);
50879     },
50880
50881     hideSplitter : function(){
50882         if(this.split){
50883             this.split.el.setLocation(-2000,-2000);
50884             this.split.el.hide();
50885         }
50886     },
50887
50888     show : function(){
50889         if(this.split){
50890             this.split.el.show();
50891         }
50892         Roo.SplitLayoutRegion.superclass.show.call(this);
50893     },
50894     
50895     beforeSlide: function(){
50896         if(Roo.isGecko){// firefox overflow auto bug workaround
50897             this.bodyEl.clip();
50898             if(this.tabs) this.tabs.bodyEl.clip();
50899             if(this.activePanel){
50900                 this.activePanel.getEl().clip();
50901                 
50902                 if(this.activePanel.beforeSlide){
50903                     this.activePanel.beforeSlide();
50904                 }
50905             }
50906         }
50907     },
50908     
50909     afterSlide : function(){
50910         if(Roo.isGecko){// firefox overflow auto bug workaround
50911             this.bodyEl.unclip();
50912             if(this.tabs) this.tabs.bodyEl.unclip();
50913             if(this.activePanel){
50914                 this.activePanel.getEl().unclip();
50915                 if(this.activePanel.afterSlide){
50916                     this.activePanel.afterSlide();
50917                 }
50918             }
50919         }
50920     },
50921
50922     initAutoHide : function(){
50923         if(this.autoHide !== false){
50924             if(!this.autoHideHd){
50925                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50926                 this.autoHideHd = {
50927                     "mouseout": function(e){
50928                         if(!e.within(this.el, true)){
50929                             st.delay(500);
50930                         }
50931                     },
50932                     "mouseover" : function(e){
50933                         st.cancel();
50934                     },
50935                     scope : this
50936                 };
50937             }
50938             this.el.on(this.autoHideHd);
50939         }
50940     },
50941
50942     clearAutoHide : function(){
50943         if(this.autoHide !== false){
50944             this.el.un("mouseout", this.autoHideHd.mouseout);
50945             this.el.un("mouseover", this.autoHideHd.mouseover);
50946         }
50947     },
50948
50949     clearMonitor : function(){
50950         Roo.get(document).un("click", this.slideInIf, this);
50951     },
50952
50953     // these names are backwards but not changed for compat
50954     slideOut : function(){
50955         if(this.isSlid || this.el.hasActiveFx()){
50956             return;
50957         }
50958         this.isSlid = true;
50959         if(this.collapseBtn){
50960             this.collapseBtn.hide();
50961         }
50962         this.closeBtnState = this.closeBtn.getStyle('display');
50963         this.closeBtn.hide();
50964         if(this.stickBtn){
50965             this.stickBtn.show();
50966         }
50967         this.el.show();
50968         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50969         this.beforeSlide();
50970         this.el.setStyle("z-index", 10001);
50971         this.el.slideIn(this.getSlideAnchor(), {
50972             callback: function(){
50973                 this.afterSlide();
50974                 this.initAutoHide();
50975                 Roo.get(document).on("click", this.slideInIf, this);
50976                 this.fireEvent("slideshow", this);
50977             },
50978             scope: this,
50979             block: true
50980         });
50981     },
50982
50983     afterSlideIn : function(){
50984         this.clearAutoHide();
50985         this.isSlid = false;
50986         this.clearMonitor();
50987         this.el.setStyle("z-index", "");
50988         if(this.collapseBtn){
50989             this.collapseBtn.show();
50990         }
50991         this.closeBtn.setStyle('display', this.closeBtnState);
50992         if(this.stickBtn){
50993             this.stickBtn.hide();
50994         }
50995         this.fireEvent("slidehide", this);
50996     },
50997
50998     slideIn : function(cb){
50999         if(!this.isSlid || this.el.hasActiveFx()){
51000             Roo.callback(cb);
51001             return;
51002         }
51003         this.isSlid = false;
51004         this.beforeSlide();
51005         this.el.slideOut(this.getSlideAnchor(), {
51006             callback: function(){
51007                 this.el.setLeftTop(-10000, -10000);
51008                 this.afterSlide();
51009                 this.afterSlideIn();
51010                 Roo.callback(cb);
51011             },
51012             scope: this,
51013             block: true
51014         });
51015     },
51016     
51017     slideInIf : function(e){
51018         if(!e.within(this.el)){
51019             this.slideIn();
51020         }
51021     },
51022
51023     animateCollapse : function(){
51024         this.beforeSlide();
51025         this.el.setStyle("z-index", 20000);
51026         var anchor = this.getSlideAnchor();
51027         this.el.slideOut(anchor, {
51028             callback : function(){
51029                 this.el.setStyle("z-index", "");
51030                 this.collapsedEl.slideIn(anchor, {duration:.3});
51031                 this.afterSlide();
51032                 this.el.setLocation(-10000,-10000);
51033                 this.el.hide();
51034                 this.fireEvent("collapsed", this);
51035             },
51036             scope: this,
51037             block: true
51038         });
51039     },
51040
51041     animateExpand : function(){
51042         this.beforeSlide();
51043         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51044         this.el.setStyle("z-index", 20000);
51045         this.collapsedEl.hide({
51046             duration:.1
51047         });
51048         this.el.slideIn(this.getSlideAnchor(), {
51049             callback : function(){
51050                 this.el.setStyle("z-index", "");
51051                 this.afterSlide();
51052                 if(this.split){
51053                     this.split.el.show();
51054                 }
51055                 this.fireEvent("invalidated", this);
51056                 this.fireEvent("expanded", this);
51057             },
51058             scope: this,
51059             block: true
51060         });
51061     },
51062
51063     anchors : {
51064         "west" : "left",
51065         "east" : "right",
51066         "north" : "top",
51067         "south" : "bottom"
51068     },
51069
51070     sanchors : {
51071         "west" : "l",
51072         "east" : "r",
51073         "north" : "t",
51074         "south" : "b"
51075     },
51076
51077     canchors : {
51078         "west" : "tl-tr",
51079         "east" : "tr-tl",
51080         "north" : "tl-bl",
51081         "south" : "bl-tl"
51082     },
51083
51084     getAnchor : function(){
51085         return this.anchors[this.position];
51086     },
51087
51088     getCollapseAnchor : function(){
51089         return this.canchors[this.position];
51090     },
51091
51092     getSlideAnchor : function(){
51093         return this.sanchors[this.position];
51094     },
51095
51096     getAlignAdj : function(){
51097         var cm = this.cmargins;
51098         switch(this.position){
51099             case "west":
51100                 return [0, 0];
51101             break;
51102             case "east":
51103                 return [0, 0];
51104             break;
51105             case "north":
51106                 return [0, 0];
51107             break;
51108             case "south":
51109                 return [0, 0];
51110             break;
51111         }
51112     },
51113
51114     getExpandAdj : function(){
51115         var c = this.collapsedEl, cm = this.cmargins;
51116         switch(this.position){
51117             case "west":
51118                 return [-(cm.right+c.getWidth()+cm.left), 0];
51119             break;
51120             case "east":
51121                 return [cm.right+c.getWidth()+cm.left, 0];
51122             break;
51123             case "north":
51124                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51125             break;
51126             case "south":
51127                 return [0, cm.top+cm.bottom+c.getHeight()];
51128             break;
51129         }
51130     }
51131 });/*
51132  * Based on:
51133  * Ext JS Library 1.1.1
51134  * Copyright(c) 2006-2007, Ext JS, LLC.
51135  *
51136  * Originally Released Under LGPL - original licence link has changed is not relivant.
51137  *
51138  * Fork - LGPL
51139  * <script type="text/javascript">
51140  */
51141 /*
51142  * These classes are private internal classes
51143  */
51144 Roo.CenterLayoutRegion = function(mgr, config){
51145     Roo.LayoutRegion.call(this, mgr, config, "center");
51146     this.visible = true;
51147     this.minWidth = config.minWidth || 20;
51148     this.minHeight = config.minHeight || 20;
51149 };
51150
51151 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51152     hide : function(){
51153         // center panel can't be hidden
51154     },
51155     
51156     show : function(){
51157         // center panel can't be hidden
51158     },
51159     
51160     getMinWidth: function(){
51161         return this.minWidth;
51162     },
51163     
51164     getMinHeight: function(){
51165         return this.minHeight;
51166     }
51167 });
51168
51169
51170 Roo.NorthLayoutRegion = function(mgr, config){
51171     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51172     if(this.split){
51173         this.split.placement = Roo.SplitBar.TOP;
51174         this.split.orientation = Roo.SplitBar.VERTICAL;
51175         this.split.el.addClass("x-layout-split-v");
51176     }
51177     var size = config.initialSize || config.height;
51178     if(typeof size != "undefined"){
51179         this.el.setHeight(size);
51180     }
51181 };
51182 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51183     orientation: Roo.SplitBar.VERTICAL,
51184     getBox : function(){
51185         if(this.collapsed){
51186             return this.collapsedEl.getBox();
51187         }
51188         var box = this.el.getBox();
51189         if(this.split){
51190             box.height += this.split.el.getHeight();
51191         }
51192         return box;
51193     },
51194     
51195     updateBox : function(box){
51196         if(this.split && !this.collapsed){
51197             box.height -= this.split.el.getHeight();
51198             this.split.el.setLeft(box.x);
51199             this.split.el.setTop(box.y+box.height);
51200             this.split.el.setWidth(box.width);
51201         }
51202         if(this.collapsed){
51203             this.updateBody(box.width, null);
51204         }
51205         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51206     }
51207 });
51208
51209 Roo.SouthLayoutRegion = function(mgr, config){
51210     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51211     if(this.split){
51212         this.split.placement = Roo.SplitBar.BOTTOM;
51213         this.split.orientation = Roo.SplitBar.VERTICAL;
51214         this.split.el.addClass("x-layout-split-v");
51215     }
51216     var size = config.initialSize || config.height;
51217     if(typeof size != "undefined"){
51218         this.el.setHeight(size);
51219     }
51220 };
51221 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51222     orientation: Roo.SplitBar.VERTICAL,
51223     getBox : function(){
51224         if(this.collapsed){
51225             return this.collapsedEl.getBox();
51226         }
51227         var box = this.el.getBox();
51228         if(this.split){
51229             var sh = this.split.el.getHeight();
51230             box.height += sh;
51231             box.y -= sh;
51232         }
51233         return box;
51234     },
51235     
51236     updateBox : function(box){
51237         if(this.split && !this.collapsed){
51238             var sh = this.split.el.getHeight();
51239             box.height -= sh;
51240             box.y += sh;
51241             this.split.el.setLeft(box.x);
51242             this.split.el.setTop(box.y-sh);
51243             this.split.el.setWidth(box.width);
51244         }
51245         if(this.collapsed){
51246             this.updateBody(box.width, null);
51247         }
51248         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51249     }
51250 });
51251
51252 Roo.EastLayoutRegion = function(mgr, config){
51253     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51254     if(this.split){
51255         this.split.placement = Roo.SplitBar.RIGHT;
51256         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51257         this.split.el.addClass("x-layout-split-h");
51258     }
51259     var size = config.initialSize || config.width;
51260     if(typeof size != "undefined"){
51261         this.el.setWidth(size);
51262     }
51263 };
51264 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51265     orientation: Roo.SplitBar.HORIZONTAL,
51266     getBox : function(){
51267         if(this.collapsed){
51268             return this.collapsedEl.getBox();
51269         }
51270         var box = this.el.getBox();
51271         if(this.split){
51272             var sw = this.split.el.getWidth();
51273             box.width += sw;
51274             box.x -= sw;
51275         }
51276         return box;
51277     },
51278
51279     updateBox : function(box){
51280         if(this.split && !this.collapsed){
51281             var sw = this.split.el.getWidth();
51282             box.width -= sw;
51283             this.split.el.setLeft(box.x);
51284             this.split.el.setTop(box.y);
51285             this.split.el.setHeight(box.height);
51286             box.x += sw;
51287         }
51288         if(this.collapsed){
51289             this.updateBody(null, box.height);
51290         }
51291         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51292     }
51293 });
51294
51295 Roo.WestLayoutRegion = function(mgr, config){
51296     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51297     if(this.split){
51298         this.split.placement = Roo.SplitBar.LEFT;
51299         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51300         this.split.el.addClass("x-layout-split-h");
51301     }
51302     var size = config.initialSize || config.width;
51303     if(typeof size != "undefined"){
51304         this.el.setWidth(size);
51305     }
51306 };
51307 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51308     orientation: Roo.SplitBar.HORIZONTAL,
51309     getBox : function(){
51310         if(this.collapsed){
51311             return this.collapsedEl.getBox();
51312         }
51313         var box = this.el.getBox();
51314         if(this.split){
51315             box.width += this.split.el.getWidth();
51316         }
51317         return box;
51318     },
51319     
51320     updateBox : function(box){
51321         if(this.split && !this.collapsed){
51322             var sw = this.split.el.getWidth();
51323             box.width -= sw;
51324             this.split.el.setLeft(box.x+box.width);
51325             this.split.el.setTop(box.y);
51326             this.split.el.setHeight(box.height);
51327         }
51328         if(this.collapsed){
51329             this.updateBody(null, box.height);
51330         }
51331         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51332     }
51333 });
51334 /*
51335  * Based on:
51336  * Ext JS Library 1.1.1
51337  * Copyright(c) 2006-2007, Ext JS, LLC.
51338  *
51339  * Originally Released Under LGPL - original licence link has changed is not relivant.
51340  *
51341  * Fork - LGPL
51342  * <script type="text/javascript">
51343  */
51344  
51345  
51346 /*
51347  * Private internal class for reading and applying state
51348  */
51349 Roo.LayoutStateManager = function(layout){
51350      // default empty state
51351      this.state = {
51352         north: {},
51353         south: {},
51354         east: {},
51355         west: {}       
51356     };
51357 };
51358
51359 Roo.LayoutStateManager.prototype = {
51360     init : function(layout, provider){
51361         this.provider = provider;
51362         var state = provider.get(layout.id+"-layout-state");
51363         if(state){
51364             var wasUpdating = layout.isUpdating();
51365             if(!wasUpdating){
51366                 layout.beginUpdate();
51367             }
51368             for(var key in state){
51369                 if(typeof state[key] != "function"){
51370                     var rstate = state[key];
51371                     var r = layout.getRegion(key);
51372                     if(r && rstate){
51373                         if(rstate.size){
51374                             r.resizeTo(rstate.size);
51375                         }
51376                         if(rstate.collapsed == true){
51377                             r.collapse(true);
51378                         }else{
51379                             r.expand(null, true);
51380                         }
51381                     }
51382                 }
51383             }
51384             if(!wasUpdating){
51385                 layout.endUpdate();
51386             }
51387             this.state = state; 
51388         }
51389         this.layout = layout;
51390         layout.on("regionresized", this.onRegionResized, this);
51391         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51392         layout.on("regionexpanded", this.onRegionExpanded, this);
51393     },
51394     
51395     storeState : function(){
51396         this.provider.set(this.layout.id+"-layout-state", this.state);
51397     },
51398     
51399     onRegionResized : function(region, newSize){
51400         this.state[region.getPosition()].size = newSize;
51401         this.storeState();
51402     },
51403     
51404     onRegionCollapsed : function(region){
51405         this.state[region.getPosition()].collapsed = true;
51406         this.storeState();
51407     },
51408     
51409     onRegionExpanded : function(region){
51410         this.state[region.getPosition()].collapsed = false;
51411         this.storeState();
51412     }
51413 };/*
51414  * Based on:
51415  * Ext JS Library 1.1.1
51416  * Copyright(c) 2006-2007, Ext JS, LLC.
51417  *
51418  * Originally Released Under LGPL - original licence link has changed is not relivant.
51419  *
51420  * Fork - LGPL
51421  * <script type="text/javascript">
51422  */
51423 /**
51424  * @class Roo.ContentPanel
51425  * @extends Roo.util.Observable
51426  * A basic ContentPanel element.
51427  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51428  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51429  * @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
51430  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51431  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51432  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51433  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51434  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51435  * @cfg {String} title          The title for this panel
51436  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51437  * @cfg {String} url            Calls {@link #setUrl} with this value
51438  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51439  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51440  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51441  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51442
51443  * @constructor
51444  * Create a new ContentPanel.
51445  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51446  * @param {String/Object} config A string to set only the title or a config object
51447  * @param {String} content (optional) Set the HTML content for this panel
51448  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51449  */
51450 Roo.ContentPanel = function(el, config, content){
51451     
51452      
51453     /*
51454     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51455         config = el;
51456         el = Roo.id();
51457     }
51458     if (config && config.parentLayout) { 
51459         el = config.parentLayout.el.createChild(); 
51460     }
51461     */
51462     if(el.autoCreate){ // xtype is available if this is called from factory
51463         config = el;
51464         el = Roo.id();
51465     }
51466     this.el = Roo.get(el);
51467     if(!this.el && config && config.autoCreate){
51468         if(typeof config.autoCreate == "object"){
51469             if(!config.autoCreate.id){
51470                 config.autoCreate.id = config.id||el;
51471             }
51472             this.el = Roo.DomHelper.append(document.body,
51473                         config.autoCreate, true);
51474         }else{
51475             this.el = Roo.DomHelper.append(document.body,
51476                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51477         }
51478     }
51479     this.closable = false;
51480     this.loaded = false;
51481     this.active = false;
51482     if(typeof config == "string"){
51483         this.title = config;
51484     }else{
51485         Roo.apply(this, config);
51486     }
51487     
51488     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51489         this.wrapEl = this.el.wrap();
51490         this.toolbar.container = this.el.insertSibling(false, 'before');
51491         this.toolbar = new Roo.Toolbar(this.toolbar);
51492     }
51493     
51494     // xtype created footer. - not sure if will work as we normally have to render first..
51495     if (this.footer && !this.footer.el && this.footer.xtype) {
51496         if (!this.wrapEl) {
51497             this.wrapEl = this.el.wrap();
51498         }
51499     
51500         this.footer.container = this.wrapEl.createChild();
51501          
51502         this.footer = Roo.factory(this.footer, Roo);
51503         
51504     }
51505     
51506     if(this.resizeEl){
51507         this.resizeEl = Roo.get(this.resizeEl, true);
51508     }else{
51509         this.resizeEl = this.el;
51510     }
51511     // handle view.xtype
51512     
51513  
51514     
51515     
51516     this.addEvents({
51517         /**
51518          * @event activate
51519          * Fires when this panel is activated. 
51520          * @param {Roo.ContentPanel} this
51521          */
51522         "activate" : true,
51523         /**
51524          * @event deactivate
51525          * Fires when this panel is activated. 
51526          * @param {Roo.ContentPanel} this
51527          */
51528         "deactivate" : true,
51529
51530         /**
51531          * @event resize
51532          * Fires when this panel is resized if fitToFrame is true.
51533          * @param {Roo.ContentPanel} this
51534          * @param {Number} width The width after any component adjustments
51535          * @param {Number} height The height after any component adjustments
51536          */
51537         "resize" : true,
51538         
51539          /**
51540          * @event render
51541          * Fires when this tab is created
51542          * @param {Roo.ContentPanel} this
51543          */
51544         "render" : true
51545         
51546         
51547         
51548     });
51549     
51550
51551     
51552     
51553     if(this.autoScroll){
51554         this.resizeEl.setStyle("overflow", "auto");
51555     } else {
51556         // fix randome scrolling
51557         this.el.on('scroll', function() {
51558             Roo.log('fix random scolling');
51559             this.scrollTo('top',0); 
51560         });
51561     }
51562     content = content || this.content;
51563     if(content){
51564         this.setContent(content);
51565     }
51566     if(config && config.url){
51567         this.setUrl(this.url, this.params, this.loadOnce);
51568     }
51569     
51570     
51571     
51572     Roo.ContentPanel.superclass.constructor.call(this);
51573     
51574     if (this.view && typeof(this.view.xtype) != 'undefined') {
51575         this.view.el = this.el.appendChild(document.createElement("div"));
51576         this.view = Roo.factory(this.view); 
51577         this.view.render  &&  this.view.render(false, '');  
51578     }
51579     
51580     
51581     this.fireEvent('render', this);
51582 };
51583
51584 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51585     tabTip:'',
51586     setRegion : function(region){
51587         this.region = region;
51588         if(region){
51589            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51590         }else{
51591            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51592         } 
51593     },
51594     
51595     /**
51596      * Returns the toolbar for this Panel if one was configured. 
51597      * @return {Roo.Toolbar} 
51598      */
51599     getToolbar : function(){
51600         return this.toolbar;
51601     },
51602     
51603     setActiveState : function(active){
51604         this.active = active;
51605         if(!active){
51606             this.fireEvent("deactivate", this);
51607         }else{
51608             this.fireEvent("activate", this);
51609         }
51610     },
51611     /**
51612      * Updates this panel's element
51613      * @param {String} content The new content
51614      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51615     */
51616     setContent : function(content, loadScripts){
51617         this.el.update(content, loadScripts);
51618     },
51619
51620     ignoreResize : function(w, h){
51621         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51622             return true;
51623         }else{
51624             this.lastSize = {width: w, height: h};
51625             return false;
51626         }
51627     },
51628     /**
51629      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51630      * @return {Roo.UpdateManager} The UpdateManager
51631      */
51632     getUpdateManager : function(){
51633         return this.el.getUpdateManager();
51634     },
51635      /**
51636      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51637      * @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:
51638 <pre><code>
51639 panel.load({
51640     url: "your-url.php",
51641     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51642     callback: yourFunction,
51643     scope: yourObject, //(optional scope)
51644     discardUrl: false,
51645     nocache: false,
51646     text: "Loading...",
51647     timeout: 30,
51648     scripts: false
51649 });
51650 </code></pre>
51651      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51652      * 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.
51653      * @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}
51654      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51655      * @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.
51656      * @return {Roo.ContentPanel} this
51657      */
51658     load : function(){
51659         var um = this.el.getUpdateManager();
51660         um.update.apply(um, arguments);
51661         return this;
51662     },
51663
51664
51665     /**
51666      * 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.
51667      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51668      * @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)
51669      * @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)
51670      * @return {Roo.UpdateManager} The UpdateManager
51671      */
51672     setUrl : function(url, params, loadOnce){
51673         if(this.refreshDelegate){
51674             this.removeListener("activate", this.refreshDelegate);
51675         }
51676         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51677         this.on("activate", this.refreshDelegate);
51678         return this.el.getUpdateManager();
51679     },
51680     
51681     _handleRefresh : function(url, params, loadOnce){
51682         if(!loadOnce || !this.loaded){
51683             var updater = this.el.getUpdateManager();
51684             updater.update(url, params, this._setLoaded.createDelegate(this));
51685         }
51686     },
51687     
51688     _setLoaded : function(){
51689         this.loaded = true;
51690     }, 
51691     
51692     /**
51693      * Returns this panel's id
51694      * @return {String} 
51695      */
51696     getId : function(){
51697         return this.el.id;
51698     },
51699     
51700     /** 
51701      * Returns this panel's element - used by regiosn to add.
51702      * @return {Roo.Element} 
51703      */
51704     getEl : function(){
51705         return this.wrapEl || this.el;
51706     },
51707     
51708     adjustForComponents : function(width, height)
51709     {
51710         //Roo.log('adjustForComponents ');
51711         if(this.resizeEl != this.el){
51712             width -= this.el.getFrameWidth('lr');
51713             height -= this.el.getFrameWidth('tb');
51714         }
51715         if(this.toolbar){
51716             var te = this.toolbar.getEl();
51717             height -= te.getHeight();
51718             te.setWidth(width);
51719         }
51720         if(this.footer){
51721             var te = this.footer.getEl();
51722             Roo.log("footer:" + te.getHeight());
51723             
51724             height -= te.getHeight();
51725             te.setWidth(width);
51726         }
51727         
51728         
51729         if(this.adjustments){
51730             width += this.adjustments[0];
51731             height += this.adjustments[1];
51732         }
51733         return {"width": width, "height": height};
51734     },
51735     
51736     setSize : function(width, height){
51737         if(this.fitToFrame && !this.ignoreResize(width, height)){
51738             if(this.fitContainer && this.resizeEl != this.el){
51739                 this.el.setSize(width, height);
51740             }
51741             var size = this.adjustForComponents(width, height);
51742             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51743             this.fireEvent('resize', this, size.width, size.height);
51744         }
51745     },
51746     
51747     /**
51748      * Returns this panel's title
51749      * @return {String} 
51750      */
51751     getTitle : function(){
51752         return this.title;
51753     },
51754     
51755     /**
51756      * Set this panel's title
51757      * @param {String} title
51758      */
51759     setTitle : function(title){
51760         this.title = title;
51761         if(this.region){
51762             this.region.updatePanelTitle(this, title);
51763         }
51764     },
51765     
51766     /**
51767      * Returns true is this panel was configured to be closable
51768      * @return {Boolean} 
51769      */
51770     isClosable : function(){
51771         return this.closable;
51772     },
51773     
51774     beforeSlide : function(){
51775         this.el.clip();
51776         this.resizeEl.clip();
51777     },
51778     
51779     afterSlide : function(){
51780         this.el.unclip();
51781         this.resizeEl.unclip();
51782     },
51783     
51784     /**
51785      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51786      *   Will fail silently if the {@link #setUrl} method has not been called.
51787      *   This does not activate the panel, just updates its content.
51788      */
51789     refresh : function(){
51790         if(this.refreshDelegate){
51791            this.loaded = false;
51792            this.refreshDelegate();
51793         }
51794     },
51795     
51796     /**
51797      * Destroys this panel
51798      */
51799     destroy : function(){
51800         this.el.removeAllListeners();
51801         var tempEl = document.createElement("span");
51802         tempEl.appendChild(this.el.dom);
51803         tempEl.innerHTML = "";
51804         this.el.remove();
51805         this.el = null;
51806     },
51807     
51808     /**
51809      * form - if the content panel contains a form - this is a reference to it.
51810      * @type {Roo.form.Form}
51811      */
51812     form : false,
51813     /**
51814      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51815      *    This contains a reference to it.
51816      * @type {Roo.View}
51817      */
51818     view : false,
51819     
51820       /**
51821      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51822      * <pre><code>
51823
51824 layout.addxtype({
51825        xtype : 'Form',
51826        items: [ .... ]
51827    }
51828 );
51829
51830 </code></pre>
51831      * @param {Object} cfg Xtype definition of item to add.
51832      */
51833     
51834     addxtype : function(cfg) {
51835         // add form..
51836         if (cfg.xtype.match(/^Form$/)) {
51837             
51838             var el;
51839             //if (this.footer) {
51840             //    el = this.footer.container.insertSibling(false, 'before');
51841             //} else {
51842                 el = this.el.createChild();
51843             //}
51844
51845             this.form = new  Roo.form.Form(cfg);
51846             
51847             
51848             if ( this.form.allItems.length) this.form.render(el.dom);
51849             return this.form;
51850         }
51851         // should only have one of theses..
51852         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51853             // views.. should not be just added - used named prop 'view''
51854             
51855             cfg.el = this.el.appendChild(document.createElement("div"));
51856             // factory?
51857             
51858             var ret = new Roo.factory(cfg);
51859              
51860              ret.render && ret.render(false, ''); // render blank..
51861             this.view = ret;
51862             return ret;
51863         }
51864         return false;
51865     }
51866 });
51867
51868 /**
51869  * @class Roo.GridPanel
51870  * @extends Roo.ContentPanel
51871  * @constructor
51872  * Create a new GridPanel.
51873  * @param {Roo.grid.Grid} grid The grid for this panel
51874  * @param {String/Object} config A string to set only the panel's title, or a config object
51875  */
51876 Roo.GridPanel = function(grid, config){
51877     
51878   
51879     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51880         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51881         
51882     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51883     
51884     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51885     
51886     if(this.toolbar){
51887         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51888     }
51889     // xtype created footer. - not sure if will work as we normally have to render first..
51890     if (this.footer && !this.footer.el && this.footer.xtype) {
51891         
51892         this.footer.container = this.grid.getView().getFooterPanel(true);
51893         this.footer.dataSource = this.grid.dataSource;
51894         this.footer = Roo.factory(this.footer, Roo);
51895         
51896     }
51897     
51898     grid.monitorWindowResize = false; // turn off autosizing
51899     grid.autoHeight = false;
51900     grid.autoWidth = false;
51901     this.grid = grid;
51902     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51903 };
51904
51905 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51906     getId : function(){
51907         return this.grid.id;
51908     },
51909     
51910     /**
51911      * Returns the grid for this panel
51912      * @return {Roo.grid.Grid} 
51913      */
51914     getGrid : function(){
51915         return this.grid;    
51916     },
51917     
51918     setSize : function(width, height){
51919         if(!this.ignoreResize(width, height)){
51920             var grid = this.grid;
51921             var size = this.adjustForComponents(width, height);
51922             grid.getGridEl().setSize(size.width, size.height);
51923             grid.autoSize();
51924         }
51925     },
51926     
51927     beforeSlide : function(){
51928         this.grid.getView().scroller.clip();
51929     },
51930     
51931     afterSlide : function(){
51932         this.grid.getView().scroller.unclip();
51933     },
51934     
51935     destroy : function(){
51936         this.grid.destroy();
51937         delete this.grid;
51938         Roo.GridPanel.superclass.destroy.call(this); 
51939     }
51940 });
51941
51942
51943 /**
51944  * @class Roo.NestedLayoutPanel
51945  * @extends Roo.ContentPanel
51946  * @constructor
51947  * Create a new NestedLayoutPanel.
51948  * 
51949  * 
51950  * @param {Roo.BorderLayout} layout The layout for this panel
51951  * @param {String/Object} config A string to set only the title or a config object
51952  */
51953 Roo.NestedLayoutPanel = function(layout, config)
51954 {
51955     // construct with only one argument..
51956     /* FIXME - implement nicer consturctors
51957     if (layout.layout) {
51958         config = layout;
51959         layout = config.layout;
51960         delete config.layout;
51961     }
51962     if (layout.xtype && !layout.getEl) {
51963         // then layout needs constructing..
51964         layout = Roo.factory(layout, Roo);
51965     }
51966     */
51967     
51968     
51969     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51970     
51971     layout.monitorWindowResize = false; // turn off autosizing
51972     this.layout = layout;
51973     this.layout.getEl().addClass("x-layout-nested-layout");
51974     
51975     
51976     
51977     
51978 };
51979
51980 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51981
51982     setSize : function(width, height){
51983         if(!this.ignoreResize(width, height)){
51984             var size = this.adjustForComponents(width, height);
51985             var el = this.layout.getEl();
51986             el.setSize(size.width, size.height);
51987             var touch = el.dom.offsetWidth;
51988             this.layout.layout();
51989             // ie requires a double layout on the first pass
51990             if(Roo.isIE && !this.initialized){
51991                 this.initialized = true;
51992                 this.layout.layout();
51993             }
51994         }
51995     },
51996     
51997     // activate all subpanels if not currently active..
51998     
51999     setActiveState : function(active){
52000         this.active = active;
52001         if(!active){
52002             this.fireEvent("deactivate", this);
52003             return;
52004         }
52005         
52006         this.fireEvent("activate", this);
52007         // not sure if this should happen before or after..
52008         if (!this.layout) {
52009             return; // should not happen..
52010         }
52011         var reg = false;
52012         for (var r in this.layout.regions) {
52013             reg = this.layout.getRegion(r);
52014             if (reg.getActivePanel()) {
52015                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52016                 reg.setActivePanel(reg.getActivePanel());
52017                 continue;
52018             }
52019             if (!reg.panels.length) {
52020                 continue;
52021             }
52022             reg.showPanel(reg.getPanel(0));
52023         }
52024         
52025         
52026         
52027         
52028     },
52029     
52030     /**
52031      * Returns the nested BorderLayout for this panel
52032      * @return {Roo.BorderLayout} 
52033      */
52034     getLayout : function(){
52035         return this.layout;
52036     },
52037     
52038      /**
52039      * Adds a xtype elements to the layout of the nested panel
52040      * <pre><code>
52041
52042 panel.addxtype({
52043        xtype : 'ContentPanel',
52044        region: 'west',
52045        items: [ .... ]
52046    }
52047 );
52048
52049 panel.addxtype({
52050         xtype : 'NestedLayoutPanel',
52051         region: 'west',
52052         layout: {
52053            center: { },
52054            west: { }   
52055         },
52056         items : [ ... list of content panels or nested layout panels.. ]
52057    }
52058 );
52059 </code></pre>
52060      * @param {Object} cfg Xtype definition of item to add.
52061      */
52062     addxtype : function(cfg) {
52063         return this.layout.addxtype(cfg);
52064     
52065     }
52066 });
52067
52068 Roo.ScrollPanel = function(el, config, content){
52069     config = config || {};
52070     config.fitToFrame = true;
52071     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52072     
52073     this.el.dom.style.overflow = "hidden";
52074     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52075     this.el.removeClass("x-layout-inactive-content");
52076     this.el.on("mousewheel", this.onWheel, this);
52077
52078     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52079     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52080     up.unselectable(); down.unselectable();
52081     up.on("click", this.scrollUp, this);
52082     down.on("click", this.scrollDown, this);
52083     up.addClassOnOver("x-scroller-btn-over");
52084     down.addClassOnOver("x-scroller-btn-over");
52085     up.addClassOnClick("x-scroller-btn-click");
52086     down.addClassOnClick("x-scroller-btn-click");
52087     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52088
52089     this.resizeEl = this.el;
52090     this.el = wrap; this.up = up; this.down = down;
52091 };
52092
52093 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52094     increment : 100,
52095     wheelIncrement : 5,
52096     scrollUp : function(){
52097         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52098     },
52099
52100     scrollDown : function(){
52101         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52102     },
52103
52104     afterScroll : function(){
52105         var el = this.resizeEl;
52106         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52107         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52108         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52109     },
52110
52111     setSize : function(){
52112         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52113         this.afterScroll();
52114     },
52115
52116     onWheel : function(e){
52117         var d = e.getWheelDelta();
52118         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52119         this.afterScroll();
52120         e.stopEvent();
52121     },
52122
52123     setContent : function(content, loadScripts){
52124         this.resizeEl.update(content, loadScripts);
52125     }
52126
52127 });
52128
52129
52130
52131
52132
52133
52134
52135
52136
52137 /**
52138  * @class Roo.TreePanel
52139  * @extends Roo.ContentPanel
52140  * @constructor
52141  * Create a new TreePanel. - defaults to fit/scoll contents.
52142  * @param {String/Object} config A string to set only the panel's title, or a config object
52143  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52144  */
52145 Roo.TreePanel = function(config){
52146     var el = config.el;
52147     var tree = config.tree;
52148     delete config.tree; 
52149     delete config.el; // hopefull!
52150     
52151     // wrapper for IE7 strict & safari scroll issue
52152     
52153     var treeEl = el.createChild();
52154     config.resizeEl = treeEl;
52155     
52156     
52157     
52158     Roo.TreePanel.superclass.constructor.call(this, el, config);
52159  
52160  
52161     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52162     //console.log(tree);
52163     this.on('activate', function()
52164     {
52165         if (this.tree.rendered) {
52166             return;
52167         }
52168         //console.log('render tree');
52169         this.tree.render();
52170     });
52171     // this should not be needed.. - it's actually the 'el' that resizes?
52172     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52173     
52174     //this.on('resize',  function (cp, w, h) {
52175     //        this.tree.innerCt.setWidth(w);
52176     //        this.tree.innerCt.setHeight(h);
52177     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52178     //});
52179
52180         
52181     
52182 };
52183
52184 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52185     fitToFrame : true,
52186     autoScroll : true
52187 });
52188
52189
52190
52191
52192
52193
52194
52195
52196
52197
52198
52199 /*
52200  * Based on:
52201  * Ext JS Library 1.1.1
52202  * Copyright(c) 2006-2007, Ext JS, LLC.
52203  *
52204  * Originally Released Under LGPL - original licence link has changed is not relivant.
52205  *
52206  * Fork - LGPL
52207  * <script type="text/javascript">
52208  */
52209  
52210
52211 /**
52212  * @class Roo.ReaderLayout
52213  * @extends Roo.BorderLayout
52214  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52215  * center region containing two nested regions (a top one for a list view and one for item preview below),
52216  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52217  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52218  * expedites the setup of the overall layout and regions for this common application style.
52219  * Example:
52220  <pre><code>
52221 var reader = new Roo.ReaderLayout();
52222 var CP = Roo.ContentPanel;  // shortcut for adding
52223
52224 reader.beginUpdate();
52225 reader.add("north", new CP("north", "North"));
52226 reader.add("west", new CP("west", {title: "West"}));
52227 reader.add("east", new CP("east", {title: "East"}));
52228
52229 reader.regions.listView.add(new CP("listView", "List"));
52230 reader.regions.preview.add(new CP("preview", "Preview"));
52231 reader.endUpdate();
52232 </code></pre>
52233 * @constructor
52234 * Create a new ReaderLayout
52235 * @param {Object} config Configuration options
52236 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52237 * document.body if omitted)
52238 */
52239 Roo.ReaderLayout = function(config, renderTo){
52240     var c = config || {size:{}};
52241     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52242         north: c.north !== false ? Roo.apply({
52243             split:false,
52244             initialSize: 32,
52245             titlebar: false
52246         }, c.north) : false,
52247         west: c.west !== false ? Roo.apply({
52248             split:true,
52249             initialSize: 200,
52250             minSize: 175,
52251             maxSize: 400,
52252             titlebar: true,
52253             collapsible: true,
52254             animate: true,
52255             margins:{left:5,right:0,bottom:5,top:5},
52256             cmargins:{left:5,right:5,bottom:5,top:5}
52257         }, c.west) : false,
52258         east: c.east !== 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:0,right:5,bottom:5,top:5},
52267             cmargins:{left:5,right:5,bottom:5,top:5}
52268         }, c.east) : false,
52269         center: Roo.apply({
52270             tabPosition: 'top',
52271             autoScroll:false,
52272             closeOnTab: true,
52273             titlebar:false,
52274             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52275         }, c.center)
52276     });
52277
52278     this.el.addClass('x-reader');
52279
52280     this.beginUpdate();
52281
52282     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52283         south: c.preview !== false ? Roo.apply({
52284             split:true,
52285             initialSize: 200,
52286             minSize: 100,
52287             autoScroll:true,
52288             collapsible:true,
52289             titlebar: true,
52290             cmargins:{top:5,left:0, right:0, bottom:0}
52291         }, c.preview) : false,
52292         center: Roo.apply({
52293             autoScroll:false,
52294             titlebar:false,
52295             minHeight:200
52296         }, c.listView)
52297     });
52298     this.add('center', new Roo.NestedLayoutPanel(inner,
52299             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52300
52301     this.endUpdate();
52302
52303     this.regions.preview = inner.getRegion('south');
52304     this.regions.listView = inner.getRegion('center');
52305 };
52306
52307 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52308  * Based on:
52309  * Ext JS Library 1.1.1
52310  * Copyright(c) 2006-2007, Ext JS, LLC.
52311  *
52312  * Originally Released Under LGPL - original licence link has changed is not relivant.
52313  *
52314  * Fork - LGPL
52315  * <script type="text/javascript">
52316  */
52317  
52318 /**
52319  * @class Roo.grid.Grid
52320  * @extends Roo.util.Observable
52321  * This class represents the primary interface of a component based grid control.
52322  * <br><br>Usage:<pre><code>
52323  var grid = new Roo.grid.Grid("my-container-id", {
52324      ds: myDataStore,
52325      cm: myColModel,
52326      selModel: mySelectionModel,
52327      autoSizeColumns: true,
52328      monitorWindowResize: false,
52329      trackMouseOver: true
52330  });
52331  // set any options
52332  grid.render();
52333  * </code></pre>
52334  * <b>Common Problems:</b><br/>
52335  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52336  * element will correct this<br/>
52337  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52338  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52339  * are unpredictable.<br/>
52340  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52341  * grid to calculate dimensions/offsets.<br/>
52342   * @constructor
52343  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52344  * The container MUST have some type of size defined for the grid to fill. The container will be
52345  * automatically set to position relative if it isn't already.
52346  * @param {Object} config A config object that sets properties on this grid.
52347  */
52348 Roo.grid.Grid = function(container, config){
52349         // initialize the container
52350         this.container = Roo.get(container);
52351         this.container.update("");
52352         this.container.setStyle("overflow", "hidden");
52353     this.container.addClass('x-grid-container');
52354
52355     this.id = this.container.id;
52356
52357     Roo.apply(this, config);
52358     // check and correct shorthanded configs
52359     if(this.ds){
52360         this.dataSource = this.ds;
52361         delete this.ds;
52362     }
52363     if(this.cm){
52364         this.colModel = this.cm;
52365         delete this.cm;
52366     }
52367     if(this.sm){
52368         this.selModel = this.sm;
52369         delete this.sm;
52370     }
52371
52372     if (this.selModel) {
52373         this.selModel = Roo.factory(this.selModel, Roo.grid);
52374         this.sm = this.selModel;
52375         this.sm.xmodule = this.xmodule || false;
52376     }
52377     if (typeof(this.colModel.config) == 'undefined') {
52378         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52379         this.cm = this.colModel;
52380         this.cm.xmodule = this.xmodule || false;
52381     }
52382     if (this.dataSource) {
52383         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52384         this.ds = this.dataSource;
52385         this.ds.xmodule = this.xmodule || false;
52386          
52387     }
52388     
52389     
52390     
52391     if(this.width){
52392         this.container.setWidth(this.width);
52393     }
52394
52395     if(this.height){
52396         this.container.setHeight(this.height);
52397     }
52398     /** @private */
52399         this.addEvents({
52400         // raw events
52401         /**
52402          * @event click
52403          * The raw click event for the entire grid.
52404          * @param {Roo.EventObject} e
52405          */
52406         "click" : true,
52407         /**
52408          * @event dblclick
52409          * The raw dblclick event for the entire grid.
52410          * @param {Roo.EventObject} e
52411          */
52412         "dblclick" : true,
52413         /**
52414          * @event contextmenu
52415          * The raw contextmenu event for the entire grid.
52416          * @param {Roo.EventObject} e
52417          */
52418         "contextmenu" : true,
52419         /**
52420          * @event mousedown
52421          * The raw mousedown event for the entire grid.
52422          * @param {Roo.EventObject} e
52423          */
52424         "mousedown" : true,
52425         /**
52426          * @event mouseup
52427          * The raw mouseup event for the entire grid.
52428          * @param {Roo.EventObject} e
52429          */
52430         "mouseup" : true,
52431         /**
52432          * @event mouseover
52433          * The raw mouseover event for the entire grid.
52434          * @param {Roo.EventObject} e
52435          */
52436         "mouseover" : true,
52437         /**
52438          * @event mouseout
52439          * The raw mouseout event for the entire grid.
52440          * @param {Roo.EventObject} e
52441          */
52442         "mouseout" : true,
52443         /**
52444          * @event keypress
52445          * The raw keypress event for the entire grid.
52446          * @param {Roo.EventObject} e
52447          */
52448         "keypress" : true,
52449         /**
52450          * @event keydown
52451          * The raw keydown event for the entire grid.
52452          * @param {Roo.EventObject} e
52453          */
52454         "keydown" : true,
52455
52456         // custom events
52457
52458         /**
52459          * @event cellclick
52460          * Fires when a cell is clicked
52461          * @param {Grid} this
52462          * @param {Number} rowIndex
52463          * @param {Number} columnIndex
52464          * @param {Roo.EventObject} e
52465          */
52466         "cellclick" : true,
52467         /**
52468          * @event celldblclick
52469          * Fires when a cell is double clicked
52470          * @param {Grid} this
52471          * @param {Number} rowIndex
52472          * @param {Number} columnIndex
52473          * @param {Roo.EventObject} e
52474          */
52475         "celldblclick" : true,
52476         /**
52477          * @event rowclick
52478          * Fires when a row is clicked
52479          * @param {Grid} this
52480          * @param {Number} rowIndex
52481          * @param {Roo.EventObject} e
52482          */
52483         "rowclick" : true,
52484         /**
52485          * @event rowdblclick
52486          * Fires when a row is double clicked
52487          * @param {Grid} this
52488          * @param {Number} rowIndex
52489          * @param {Roo.EventObject} e
52490          */
52491         "rowdblclick" : true,
52492         /**
52493          * @event headerclick
52494          * Fires when a header is clicked
52495          * @param {Grid} this
52496          * @param {Number} columnIndex
52497          * @param {Roo.EventObject} e
52498          */
52499         "headerclick" : true,
52500         /**
52501          * @event headerdblclick
52502          * Fires when a header cell is double clicked
52503          * @param {Grid} this
52504          * @param {Number} columnIndex
52505          * @param {Roo.EventObject} e
52506          */
52507         "headerdblclick" : true,
52508         /**
52509          * @event rowcontextmenu
52510          * Fires when a row is right clicked
52511          * @param {Grid} this
52512          * @param {Number} rowIndex
52513          * @param {Roo.EventObject} e
52514          */
52515         "rowcontextmenu" : true,
52516         /**
52517          * @event cellcontextmenu
52518          * Fires when a cell is right clicked
52519          * @param {Grid} this
52520          * @param {Number} rowIndex
52521          * @param {Number} cellIndex
52522          * @param {Roo.EventObject} e
52523          */
52524          "cellcontextmenu" : true,
52525         /**
52526          * @event headercontextmenu
52527          * Fires when a header is right clicked
52528          * @param {Grid} this
52529          * @param {Number} columnIndex
52530          * @param {Roo.EventObject} e
52531          */
52532         "headercontextmenu" : true,
52533         /**
52534          * @event bodyscroll
52535          * Fires when the body element is scrolled
52536          * @param {Number} scrollLeft
52537          * @param {Number} scrollTop
52538          */
52539         "bodyscroll" : true,
52540         /**
52541          * @event columnresize
52542          * Fires when the user resizes a column
52543          * @param {Number} columnIndex
52544          * @param {Number} newSize
52545          */
52546         "columnresize" : true,
52547         /**
52548          * @event columnmove
52549          * Fires when the user moves a column
52550          * @param {Number} oldIndex
52551          * @param {Number} newIndex
52552          */
52553         "columnmove" : true,
52554         /**
52555          * @event startdrag
52556          * Fires when row(s) start being dragged
52557          * @param {Grid} this
52558          * @param {Roo.GridDD} dd The drag drop object
52559          * @param {event} e The raw browser event
52560          */
52561         "startdrag" : true,
52562         /**
52563          * @event enddrag
52564          * Fires when a drag operation is complete
52565          * @param {Grid} this
52566          * @param {Roo.GridDD} dd The drag drop object
52567          * @param {event} e The raw browser event
52568          */
52569         "enddrag" : true,
52570         /**
52571          * @event dragdrop
52572          * Fires when dragged row(s) are dropped on a valid DD target
52573          * @param {Grid} this
52574          * @param {Roo.GridDD} dd The drag drop object
52575          * @param {String} targetId The target drag drop object
52576          * @param {event} e The raw browser event
52577          */
52578         "dragdrop" : true,
52579         /**
52580          * @event dragover
52581          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52582          * @param {Grid} this
52583          * @param {Roo.GridDD} dd The drag drop object
52584          * @param {String} targetId The target drag drop object
52585          * @param {event} e The raw browser event
52586          */
52587         "dragover" : true,
52588         /**
52589          * @event dragenter
52590          *  Fires when the dragged row(s) first cross another DD target while being dragged
52591          * @param {Grid} this
52592          * @param {Roo.GridDD} dd The drag drop object
52593          * @param {String} targetId The target drag drop object
52594          * @param {event} e The raw browser event
52595          */
52596         "dragenter" : true,
52597         /**
52598          * @event dragout
52599          * Fires when the dragged row(s) leave another DD target while being dragged
52600          * @param {Grid} this
52601          * @param {Roo.GridDD} dd The drag drop object
52602          * @param {String} targetId The target drag drop object
52603          * @param {event} e The raw browser event
52604          */
52605         "dragout" : true,
52606         /**
52607          * @event rowclass
52608          * Fires when a row is rendered, so you can change add a style to it.
52609          * @param {GridView} gridview   The grid view
52610          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52611          */
52612         'rowclass' : true,
52613
52614         /**
52615          * @event render
52616          * Fires when the grid is rendered
52617          * @param {Grid} grid
52618          */
52619         'render' : true
52620     });
52621
52622     Roo.grid.Grid.superclass.constructor.call(this);
52623 };
52624 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52625     
52626     /**
52627      * @cfg {String} ddGroup - drag drop group.
52628      */
52629
52630     /**
52631      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52632      */
52633     minColumnWidth : 25,
52634
52635     /**
52636      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52637      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52638      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52639      */
52640     autoSizeColumns : false,
52641
52642     /**
52643      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52644      */
52645     autoSizeHeaders : true,
52646
52647     /**
52648      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52649      */
52650     monitorWindowResize : true,
52651
52652     /**
52653      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52654      * rows measured to get a columns size. Default is 0 (all rows).
52655      */
52656     maxRowsToMeasure : 0,
52657
52658     /**
52659      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52660      */
52661     trackMouseOver : true,
52662
52663     /**
52664     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52665     */
52666     
52667     /**
52668     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52669     */
52670     enableDragDrop : false,
52671     
52672     /**
52673     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52674     */
52675     enableColumnMove : true,
52676     
52677     /**
52678     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52679     */
52680     enableColumnHide : true,
52681     
52682     /**
52683     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52684     */
52685     enableRowHeightSync : false,
52686     
52687     /**
52688     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52689     */
52690     stripeRows : true,
52691     
52692     /**
52693     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52694     */
52695     autoHeight : false,
52696
52697     /**
52698      * @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.
52699      */
52700     autoExpandColumn : false,
52701
52702     /**
52703     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52704     * Default is 50.
52705     */
52706     autoExpandMin : 50,
52707
52708     /**
52709     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52710     */
52711     autoExpandMax : 1000,
52712
52713     /**
52714     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52715     */
52716     view : null,
52717
52718     /**
52719     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52720     */
52721     loadMask : false,
52722     /**
52723     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52724     */
52725     dropTarget: false,
52726     
52727    
52728     
52729     // private
52730     rendered : false,
52731
52732     /**
52733     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52734     * of a fixed width. Default is false.
52735     */
52736     /**
52737     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52738     */
52739     /**
52740      * Called once after all setup has been completed and the grid is ready to be rendered.
52741      * @return {Roo.grid.Grid} this
52742      */
52743     render : function()
52744     {
52745         var c = this.container;
52746         // try to detect autoHeight/width mode
52747         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52748             this.autoHeight = true;
52749         }
52750         var view = this.getView();
52751         view.init(this);
52752
52753         c.on("click", this.onClick, this);
52754         c.on("dblclick", this.onDblClick, this);
52755         c.on("contextmenu", this.onContextMenu, this);
52756         c.on("keydown", this.onKeyDown, this);
52757         if (Roo.isTouch) {
52758             c.on("touchstart", this.onTouchStart, this);
52759         }
52760
52761         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52762
52763         this.getSelectionModel().init(this);
52764
52765         view.render();
52766
52767         if(this.loadMask){
52768             this.loadMask = new Roo.LoadMask(this.container,
52769                     Roo.apply({store:this.dataSource}, this.loadMask));
52770         }
52771         
52772         
52773         if (this.toolbar && this.toolbar.xtype) {
52774             this.toolbar.container = this.getView().getHeaderPanel(true);
52775             this.toolbar = new Roo.Toolbar(this.toolbar);
52776         }
52777         if (this.footer && this.footer.xtype) {
52778             this.footer.dataSource = this.getDataSource();
52779             this.footer.container = this.getView().getFooterPanel(true);
52780             this.footer = Roo.factory(this.footer, Roo);
52781         }
52782         if (this.dropTarget && this.dropTarget.xtype) {
52783             delete this.dropTarget.xtype;
52784             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52785         }
52786         
52787         
52788         this.rendered = true;
52789         this.fireEvent('render', this);
52790         return this;
52791     },
52792
52793         /**
52794          * Reconfigures the grid to use a different Store and Column Model.
52795          * The View will be bound to the new objects and refreshed.
52796          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52797          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52798          */
52799     reconfigure : function(dataSource, colModel){
52800         if(this.loadMask){
52801             this.loadMask.destroy();
52802             this.loadMask = new Roo.LoadMask(this.container,
52803                     Roo.apply({store:dataSource}, this.loadMask));
52804         }
52805         this.view.bind(dataSource, colModel);
52806         this.dataSource = dataSource;
52807         this.colModel = colModel;
52808         this.view.refresh(true);
52809     },
52810
52811     // private
52812     onKeyDown : function(e){
52813         this.fireEvent("keydown", e);
52814     },
52815
52816     /**
52817      * Destroy this grid.
52818      * @param {Boolean} removeEl True to remove the element
52819      */
52820     destroy : function(removeEl, keepListeners){
52821         if(this.loadMask){
52822             this.loadMask.destroy();
52823         }
52824         var c = this.container;
52825         c.removeAllListeners();
52826         this.view.destroy();
52827         this.colModel.purgeListeners();
52828         if(!keepListeners){
52829             this.purgeListeners();
52830         }
52831         c.update("");
52832         if(removeEl === true){
52833             c.remove();
52834         }
52835     },
52836
52837     // private
52838     processEvent : function(name, e){
52839         // does this fire select???
52840         //Roo.log('grid:processEvent '  + name);
52841         
52842         if (name != 'touchstart' ) {
52843             this.fireEvent(name, e);    
52844         }
52845         
52846         var t = e.getTarget();
52847         var v = this.view;
52848         var header = v.findHeaderIndex(t);
52849         if(header !== false){
52850             var ename = name == 'touchstart' ? 'click' : name;
52851              
52852             this.fireEvent("header" + ename, this, header, e);
52853         }else{
52854             var row = v.findRowIndex(t);
52855             var cell = v.findCellIndex(t);
52856             if (name == 'touchstart') {
52857                 // first touch is always a click.
52858                 // hopefull this happens after selection is updated.?
52859                 name = false;
52860                 
52861                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52862                     var cs = this.selModel.getSelectedCell();
52863                     if (row == cs[0] && cell == cs[1]){
52864                         name = 'dblclick';
52865                     }
52866                 }
52867                 if (typeof(this.selModel.getSelections) != 'undefined') {
52868                     var cs = this.selModel.getSelections();
52869                     var ds = this.dataSource;
52870                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52871                         name = 'dblclick';
52872                     }
52873                 }
52874                 if (!name) {
52875                     return;
52876                 }
52877             }
52878             
52879             
52880             if(row !== false){
52881                 this.fireEvent("row" + name, this, row, e);
52882                 if(cell !== false){
52883                     this.fireEvent("cell" + name, this, row, cell, e);
52884                 }
52885             }
52886         }
52887     },
52888
52889     // private
52890     onClick : function(e){
52891         this.processEvent("click", e);
52892     },
52893    // private
52894     onTouchStart : function(e){
52895         this.processEvent("touchstart", e);
52896     },
52897
52898     // private
52899     onContextMenu : function(e, t){
52900         this.processEvent("contextmenu", e);
52901     },
52902
52903     // private
52904     onDblClick : function(e){
52905         this.processEvent("dblclick", e);
52906     },
52907
52908     // private
52909     walkCells : function(row, col, step, fn, scope){
52910         var cm = this.colModel, clen = cm.getColumnCount();
52911         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52912         if(step < 0){
52913             if(col < 0){
52914                 row--;
52915                 first = false;
52916             }
52917             while(row >= 0){
52918                 if(!first){
52919                     col = clen-1;
52920                 }
52921                 first = false;
52922                 while(col >= 0){
52923                     if(fn.call(scope || this, row, col, cm) === true){
52924                         return [row, col];
52925                     }
52926                     col--;
52927                 }
52928                 row--;
52929             }
52930         } else {
52931             if(col >= clen){
52932                 row++;
52933                 first = false;
52934             }
52935             while(row < rlen){
52936                 if(!first){
52937                     col = 0;
52938                 }
52939                 first = false;
52940                 while(col < clen){
52941                     if(fn.call(scope || this, row, col, cm) === true){
52942                         return [row, col];
52943                     }
52944                     col++;
52945                 }
52946                 row++;
52947             }
52948         }
52949         return null;
52950     },
52951
52952     // private
52953     getSelections : function(){
52954         return this.selModel.getSelections();
52955     },
52956
52957     /**
52958      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52959      * but if manual update is required this method will initiate it.
52960      */
52961     autoSize : function(){
52962         if(this.rendered){
52963             this.view.layout();
52964             if(this.view.adjustForScroll){
52965                 this.view.adjustForScroll();
52966             }
52967         }
52968     },
52969
52970     /**
52971      * Returns the grid's underlying element.
52972      * @return {Element} The element
52973      */
52974     getGridEl : function(){
52975         return this.container;
52976     },
52977
52978     // private for compatibility, overridden by editor grid
52979     stopEditing : function(){},
52980
52981     /**
52982      * Returns the grid's SelectionModel.
52983      * @return {SelectionModel}
52984      */
52985     getSelectionModel : function(){
52986         if(!this.selModel){
52987             this.selModel = new Roo.grid.RowSelectionModel();
52988         }
52989         return this.selModel;
52990     },
52991
52992     /**
52993      * Returns the grid's DataSource.
52994      * @return {DataSource}
52995      */
52996     getDataSource : function(){
52997         return this.dataSource;
52998     },
52999
53000     /**
53001      * Returns the grid's ColumnModel.
53002      * @return {ColumnModel}
53003      */
53004     getColumnModel : function(){
53005         return this.colModel;
53006     },
53007
53008     /**
53009      * Returns the grid's GridView object.
53010      * @return {GridView}
53011      */
53012     getView : function(){
53013         if(!this.view){
53014             this.view = new Roo.grid.GridView(this.viewConfig);
53015         }
53016         return this.view;
53017     },
53018     /**
53019      * Called to get grid's drag proxy text, by default returns this.ddText.
53020      * @return {String}
53021      */
53022     getDragDropText : function(){
53023         var count = this.selModel.getCount();
53024         return String.format(this.ddText, count, count == 1 ? '' : 's');
53025     }
53026 });
53027 /**
53028  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53029  * %0 is replaced with the number of selected rows.
53030  * @type String
53031  */
53032 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53033  * Based on:
53034  * Ext JS Library 1.1.1
53035  * Copyright(c) 2006-2007, Ext JS, LLC.
53036  *
53037  * Originally Released Under LGPL - original licence link has changed is not relivant.
53038  *
53039  * Fork - LGPL
53040  * <script type="text/javascript">
53041  */
53042  
53043 Roo.grid.AbstractGridView = function(){
53044         this.grid = null;
53045         
53046         this.events = {
53047             "beforerowremoved" : true,
53048             "beforerowsinserted" : true,
53049             "beforerefresh" : true,
53050             "rowremoved" : true,
53051             "rowsinserted" : true,
53052             "rowupdated" : true,
53053             "refresh" : true
53054         };
53055     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53056 };
53057
53058 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53059     rowClass : "x-grid-row",
53060     cellClass : "x-grid-cell",
53061     tdClass : "x-grid-td",
53062     hdClass : "x-grid-hd",
53063     splitClass : "x-grid-hd-split",
53064     
53065     init: function(grid){
53066         this.grid = grid;
53067                 var cid = this.grid.getGridEl().id;
53068         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53069         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53070         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53071         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53072         },
53073         
53074     getColumnRenderers : function(){
53075         var renderers = [];
53076         var cm = this.grid.colModel;
53077         var colCount = cm.getColumnCount();
53078         for(var i = 0; i < colCount; i++){
53079             renderers[i] = cm.getRenderer(i);
53080         }
53081         return renderers;
53082     },
53083     
53084     getColumnIds : function(){
53085         var ids = [];
53086         var cm = this.grid.colModel;
53087         var colCount = cm.getColumnCount();
53088         for(var i = 0; i < colCount; i++){
53089             ids[i] = cm.getColumnId(i);
53090         }
53091         return ids;
53092     },
53093     
53094     getDataIndexes : function(){
53095         if(!this.indexMap){
53096             this.indexMap = this.buildIndexMap();
53097         }
53098         return this.indexMap.colToData;
53099     },
53100     
53101     getColumnIndexByDataIndex : function(dataIndex){
53102         if(!this.indexMap){
53103             this.indexMap = this.buildIndexMap();
53104         }
53105         return this.indexMap.dataToCol[dataIndex];
53106     },
53107     
53108     /**
53109      * Set a css style for a column dynamically. 
53110      * @param {Number} colIndex The index of the column
53111      * @param {String} name The css property name
53112      * @param {String} value The css value
53113      */
53114     setCSSStyle : function(colIndex, name, value){
53115         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53116         Roo.util.CSS.updateRule(selector, name, value);
53117     },
53118     
53119     generateRules : function(cm){
53120         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53121         Roo.util.CSS.removeStyleSheet(rulesId);
53122         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53123             var cid = cm.getColumnId(i);
53124             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53125                          this.tdSelector, cid, " {\n}\n",
53126                          this.hdSelector, cid, " {\n}\n",
53127                          this.splitSelector, cid, " {\n}\n");
53128         }
53129         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53130     }
53131 });/*
53132  * Based on:
53133  * Ext JS Library 1.1.1
53134  * Copyright(c) 2006-2007, Ext JS, LLC.
53135  *
53136  * Originally Released Under LGPL - original licence link has changed is not relivant.
53137  *
53138  * Fork - LGPL
53139  * <script type="text/javascript">
53140  */
53141
53142 // private
53143 // This is a support class used internally by the Grid components
53144 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53145     this.grid = grid;
53146     this.view = grid.getView();
53147     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53148     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53149     if(hd2){
53150         this.setHandleElId(Roo.id(hd));
53151         this.setOuterHandleElId(Roo.id(hd2));
53152     }
53153     this.scroll = false;
53154 };
53155 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53156     maxDragWidth: 120,
53157     getDragData : function(e){
53158         var t = Roo.lib.Event.getTarget(e);
53159         var h = this.view.findHeaderCell(t);
53160         if(h){
53161             return {ddel: h.firstChild, header:h};
53162         }
53163         return false;
53164     },
53165
53166     onInitDrag : function(e){
53167         this.view.headersDisabled = true;
53168         var clone = this.dragData.ddel.cloneNode(true);
53169         clone.id = Roo.id();
53170         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53171         this.proxy.update(clone);
53172         return true;
53173     },
53174
53175     afterValidDrop : function(){
53176         var v = this.view;
53177         setTimeout(function(){
53178             v.headersDisabled = false;
53179         }, 50);
53180     },
53181
53182     afterInvalidDrop : function(){
53183         var v = this.view;
53184         setTimeout(function(){
53185             v.headersDisabled = false;
53186         }, 50);
53187     }
53188 });
53189 /*
53190  * Based on:
53191  * Ext JS Library 1.1.1
53192  * Copyright(c) 2006-2007, Ext JS, LLC.
53193  *
53194  * Originally Released Under LGPL - original licence link has changed is not relivant.
53195  *
53196  * Fork - LGPL
53197  * <script type="text/javascript">
53198  */
53199 // private
53200 // This is a support class used internally by the Grid components
53201 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53202     this.grid = grid;
53203     this.view = grid.getView();
53204     // split the proxies so they don't interfere with mouse events
53205     this.proxyTop = Roo.DomHelper.append(document.body, {
53206         cls:"col-move-top", html:"&#160;"
53207     }, true);
53208     this.proxyBottom = Roo.DomHelper.append(document.body, {
53209         cls:"col-move-bottom", html:"&#160;"
53210     }, true);
53211     this.proxyTop.hide = this.proxyBottom.hide = function(){
53212         this.setLeftTop(-100,-100);
53213         this.setStyle("visibility", "hidden");
53214     };
53215     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53216     // temporarily disabled
53217     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53218     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53219 };
53220 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53221     proxyOffsets : [-4, -9],
53222     fly: Roo.Element.fly,
53223
53224     getTargetFromEvent : function(e){
53225         var t = Roo.lib.Event.getTarget(e);
53226         var cindex = this.view.findCellIndex(t);
53227         if(cindex !== false){
53228             return this.view.getHeaderCell(cindex);
53229         }
53230         return null;
53231     },
53232
53233     nextVisible : function(h){
53234         var v = this.view, cm = this.grid.colModel;
53235         h = h.nextSibling;
53236         while(h){
53237             if(!cm.isHidden(v.getCellIndex(h))){
53238                 return h;
53239             }
53240             h = h.nextSibling;
53241         }
53242         return null;
53243     },
53244
53245     prevVisible : function(h){
53246         var v = this.view, cm = this.grid.colModel;
53247         h = h.prevSibling;
53248         while(h){
53249             if(!cm.isHidden(v.getCellIndex(h))){
53250                 return h;
53251             }
53252             h = h.prevSibling;
53253         }
53254         return null;
53255     },
53256
53257     positionIndicator : function(h, n, e){
53258         var x = Roo.lib.Event.getPageX(e);
53259         var r = Roo.lib.Dom.getRegion(n.firstChild);
53260         var px, pt, py = r.top + this.proxyOffsets[1];
53261         if((r.right - x) <= (r.right-r.left)/2){
53262             px = r.right+this.view.borderWidth;
53263             pt = "after";
53264         }else{
53265             px = r.left;
53266             pt = "before";
53267         }
53268         var oldIndex = this.view.getCellIndex(h);
53269         var newIndex = this.view.getCellIndex(n);
53270
53271         if(this.grid.colModel.isFixed(newIndex)){
53272             return false;
53273         }
53274
53275         var locked = this.grid.colModel.isLocked(newIndex);
53276
53277         if(pt == "after"){
53278             newIndex++;
53279         }
53280         if(oldIndex < newIndex){
53281             newIndex--;
53282         }
53283         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53284             return false;
53285         }
53286         px +=  this.proxyOffsets[0];
53287         this.proxyTop.setLeftTop(px, py);
53288         this.proxyTop.show();
53289         if(!this.bottomOffset){
53290             this.bottomOffset = this.view.mainHd.getHeight();
53291         }
53292         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53293         this.proxyBottom.show();
53294         return pt;
53295     },
53296
53297     onNodeEnter : function(n, dd, e, data){
53298         if(data.header != n){
53299             this.positionIndicator(data.header, n, e);
53300         }
53301     },
53302
53303     onNodeOver : function(n, dd, e, data){
53304         var result = false;
53305         if(data.header != n){
53306             result = this.positionIndicator(data.header, n, e);
53307         }
53308         if(!result){
53309             this.proxyTop.hide();
53310             this.proxyBottom.hide();
53311         }
53312         return result ? this.dropAllowed : this.dropNotAllowed;
53313     },
53314
53315     onNodeOut : function(n, dd, e, data){
53316         this.proxyTop.hide();
53317         this.proxyBottom.hide();
53318     },
53319
53320     onNodeDrop : function(n, dd, e, data){
53321         var h = data.header;
53322         if(h != n){
53323             var cm = this.grid.colModel;
53324             var x = Roo.lib.Event.getPageX(e);
53325             var r = Roo.lib.Dom.getRegion(n.firstChild);
53326             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53327             var oldIndex = this.view.getCellIndex(h);
53328             var newIndex = this.view.getCellIndex(n);
53329             var locked = cm.isLocked(newIndex);
53330             if(pt == "after"){
53331                 newIndex++;
53332             }
53333             if(oldIndex < newIndex){
53334                 newIndex--;
53335             }
53336             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53337                 return false;
53338             }
53339             cm.setLocked(oldIndex, locked, true);
53340             cm.moveColumn(oldIndex, newIndex);
53341             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53342             return true;
53343         }
53344         return false;
53345     }
53346 });
53347 /*
53348  * Based on:
53349  * Ext JS Library 1.1.1
53350  * Copyright(c) 2006-2007, Ext JS, LLC.
53351  *
53352  * Originally Released Under LGPL - original licence link has changed is not relivant.
53353  *
53354  * Fork - LGPL
53355  * <script type="text/javascript">
53356  */
53357   
53358 /**
53359  * @class Roo.grid.GridView
53360  * @extends Roo.util.Observable
53361  *
53362  * @constructor
53363  * @param {Object} config
53364  */
53365 Roo.grid.GridView = function(config){
53366     Roo.grid.GridView.superclass.constructor.call(this);
53367     this.el = null;
53368
53369     Roo.apply(this, config);
53370 };
53371
53372 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53373
53374     unselectable :  'unselectable="on"',
53375     unselectableCls :  'x-unselectable',
53376     
53377     
53378     rowClass : "x-grid-row",
53379
53380     cellClass : "x-grid-col",
53381
53382     tdClass : "x-grid-td",
53383
53384     hdClass : "x-grid-hd",
53385
53386     splitClass : "x-grid-split",
53387
53388     sortClasses : ["sort-asc", "sort-desc"],
53389
53390     enableMoveAnim : false,
53391
53392     hlColor: "C3DAF9",
53393
53394     dh : Roo.DomHelper,
53395
53396     fly : Roo.Element.fly,
53397
53398     css : Roo.util.CSS,
53399
53400     borderWidth: 1,
53401
53402     splitOffset: 3,
53403
53404     scrollIncrement : 22,
53405
53406     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53407
53408     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53409
53410     bind : function(ds, cm){
53411         if(this.ds){
53412             this.ds.un("load", this.onLoad, this);
53413             this.ds.un("datachanged", this.onDataChange, this);
53414             this.ds.un("add", this.onAdd, this);
53415             this.ds.un("remove", this.onRemove, this);
53416             this.ds.un("update", this.onUpdate, this);
53417             this.ds.un("clear", this.onClear, this);
53418         }
53419         if(ds){
53420             ds.on("load", this.onLoad, this);
53421             ds.on("datachanged", this.onDataChange, this);
53422             ds.on("add", this.onAdd, this);
53423             ds.on("remove", this.onRemove, this);
53424             ds.on("update", this.onUpdate, this);
53425             ds.on("clear", this.onClear, this);
53426         }
53427         this.ds = ds;
53428
53429         if(this.cm){
53430             this.cm.un("widthchange", this.onColWidthChange, this);
53431             this.cm.un("headerchange", this.onHeaderChange, this);
53432             this.cm.un("hiddenchange", this.onHiddenChange, this);
53433             this.cm.un("columnmoved", this.onColumnMove, this);
53434             this.cm.un("columnlockchange", this.onColumnLock, this);
53435         }
53436         if(cm){
53437             this.generateRules(cm);
53438             cm.on("widthchange", this.onColWidthChange, this);
53439             cm.on("headerchange", this.onHeaderChange, this);
53440             cm.on("hiddenchange", this.onHiddenChange, this);
53441             cm.on("columnmoved", this.onColumnMove, this);
53442             cm.on("columnlockchange", this.onColumnLock, this);
53443         }
53444         this.cm = cm;
53445     },
53446
53447     init: function(grid){
53448         Roo.grid.GridView.superclass.init.call(this, grid);
53449
53450         this.bind(grid.dataSource, grid.colModel);
53451
53452         grid.on("headerclick", this.handleHeaderClick, this);
53453
53454         if(grid.trackMouseOver){
53455             grid.on("mouseover", this.onRowOver, this);
53456             grid.on("mouseout", this.onRowOut, this);
53457         }
53458         grid.cancelTextSelection = function(){};
53459         this.gridId = grid.id;
53460
53461         var tpls = this.templates || {};
53462
53463         if(!tpls.master){
53464             tpls.master = new Roo.Template(
53465                '<div class="x-grid" hidefocus="true">',
53466                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53467                   '<div class="x-grid-topbar"></div>',
53468                   '<div class="x-grid-scroller"><div></div></div>',
53469                   '<div class="x-grid-locked">',
53470                       '<div class="x-grid-header">{lockedHeader}</div>',
53471                       '<div class="x-grid-body">{lockedBody}</div>',
53472                   "</div>",
53473                   '<div class="x-grid-viewport">',
53474                       '<div class="x-grid-header">{header}</div>',
53475                       '<div class="x-grid-body">{body}</div>',
53476                   "</div>",
53477                   '<div class="x-grid-bottombar"></div>',
53478                  
53479                   '<div class="x-grid-resize-proxy">&#160;</div>',
53480                "</div>"
53481             );
53482             tpls.master.disableformats = true;
53483         }
53484
53485         if(!tpls.header){
53486             tpls.header = new Roo.Template(
53487                '<table border="0" cellspacing="0" cellpadding="0">',
53488                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53489                "</table>{splits}"
53490             );
53491             tpls.header.disableformats = true;
53492         }
53493         tpls.header.compile();
53494
53495         if(!tpls.hcell){
53496             tpls.hcell = new Roo.Template(
53497                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53498                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53499                 "</div></td>"
53500              );
53501              tpls.hcell.disableFormats = true;
53502         }
53503         tpls.hcell.compile();
53504
53505         if(!tpls.hsplit){
53506             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53507                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53508             tpls.hsplit.disableFormats = true;
53509         }
53510         tpls.hsplit.compile();
53511
53512         if(!tpls.body){
53513             tpls.body = new Roo.Template(
53514                '<table border="0" cellspacing="0" cellpadding="0">',
53515                "<tbody>{rows}</tbody>",
53516                "</table>"
53517             );
53518             tpls.body.disableFormats = true;
53519         }
53520         tpls.body.compile();
53521
53522         if(!tpls.row){
53523             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53524             tpls.row.disableFormats = true;
53525         }
53526         tpls.row.compile();
53527
53528         if(!tpls.cell){
53529             tpls.cell = new Roo.Template(
53530                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53531                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53532                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53533                 "</td>"
53534             );
53535             tpls.cell.disableFormats = true;
53536         }
53537         tpls.cell.compile();
53538
53539         this.templates = tpls;
53540     },
53541
53542     // remap these for backwards compat
53543     onColWidthChange : function(){
53544         this.updateColumns.apply(this, arguments);
53545     },
53546     onHeaderChange : function(){
53547         this.updateHeaders.apply(this, arguments);
53548     }, 
53549     onHiddenChange : function(){
53550         this.handleHiddenChange.apply(this, arguments);
53551     },
53552     onColumnMove : function(){
53553         this.handleColumnMove.apply(this, arguments);
53554     },
53555     onColumnLock : function(){
53556         this.handleLockChange.apply(this, arguments);
53557     },
53558
53559     onDataChange : function(){
53560         this.refresh();
53561         this.updateHeaderSortState();
53562     },
53563
53564     onClear : function(){
53565         this.refresh();
53566     },
53567
53568     onUpdate : function(ds, record){
53569         this.refreshRow(record);
53570     },
53571
53572     refreshRow : function(record){
53573         var ds = this.ds, index;
53574         if(typeof record == 'number'){
53575             index = record;
53576             record = ds.getAt(index);
53577         }else{
53578             index = ds.indexOf(record);
53579         }
53580         this.insertRows(ds, index, index, true);
53581         this.onRemove(ds, record, index+1, true);
53582         this.syncRowHeights(index, index);
53583         this.layout();
53584         this.fireEvent("rowupdated", this, index, record);
53585     },
53586
53587     onAdd : function(ds, records, index){
53588         this.insertRows(ds, index, index + (records.length-1));
53589     },
53590
53591     onRemove : function(ds, record, index, isUpdate){
53592         if(isUpdate !== true){
53593             this.fireEvent("beforerowremoved", this, index, record);
53594         }
53595         var bt = this.getBodyTable(), lt = this.getLockedTable();
53596         if(bt.rows[index]){
53597             bt.firstChild.removeChild(bt.rows[index]);
53598         }
53599         if(lt.rows[index]){
53600             lt.firstChild.removeChild(lt.rows[index]);
53601         }
53602         if(isUpdate !== true){
53603             this.stripeRows(index);
53604             this.syncRowHeights(index, index);
53605             this.layout();
53606             this.fireEvent("rowremoved", this, index, record);
53607         }
53608     },
53609
53610     onLoad : function(){
53611         this.scrollToTop();
53612     },
53613
53614     /**
53615      * Scrolls the grid to the top
53616      */
53617     scrollToTop : function(){
53618         if(this.scroller){
53619             this.scroller.dom.scrollTop = 0;
53620             this.syncScroll();
53621         }
53622     },
53623
53624     /**
53625      * Gets a panel in the header of the grid that can be used for toolbars etc.
53626      * After modifying the contents of this panel a call to grid.autoSize() may be
53627      * required to register any changes in size.
53628      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53629      * @return Roo.Element
53630      */
53631     getHeaderPanel : function(doShow){
53632         if(doShow){
53633             this.headerPanel.show();
53634         }
53635         return this.headerPanel;
53636     },
53637
53638     /**
53639      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53640      * After modifying the contents of this panel a call to grid.autoSize() may be
53641      * required to register any changes in size.
53642      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53643      * @return Roo.Element
53644      */
53645     getFooterPanel : function(doShow){
53646         if(doShow){
53647             this.footerPanel.show();
53648         }
53649         return this.footerPanel;
53650     },
53651
53652     initElements : function(){
53653         var E = Roo.Element;
53654         var el = this.grid.getGridEl().dom.firstChild;
53655         var cs = el.childNodes;
53656
53657         this.el = new E(el);
53658         
53659          this.focusEl = new E(el.firstChild);
53660         this.focusEl.swallowEvent("click", true);
53661         
53662         this.headerPanel = new E(cs[1]);
53663         this.headerPanel.enableDisplayMode("block");
53664
53665         this.scroller = new E(cs[2]);
53666         this.scrollSizer = new E(this.scroller.dom.firstChild);
53667
53668         this.lockedWrap = new E(cs[3]);
53669         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53670         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53671
53672         this.mainWrap = new E(cs[4]);
53673         this.mainHd = new E(this.mainWrap.dom.firstChild);
53674         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53675
53676         this.footerPanel = new E(cs[5]);
53677         this.footerPanel.enableDisplayMode("block");
53678
53679         this.resizeProxy = new E(cs[6]);
53680
53681         this.headerSelector = String.format(
53682            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53683            this.lockedHd.id, this.mainHd.id
53684         );
53685
53686         this.splitterSelector = String.format(
53687            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53688            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53689         );
53690     },
53691     idToCssName : function(s)
53692     {
53693         return s.replace(/[^a-z0-9]+/ig, '-');
53694     },
53695
53696     getHeaderCell : function(index){
53697         return Roo.DomQuery.select(this.headerSelector)[index];
53698     },
53699
53700     getHeaderCellMeasure : function(index){
53701         return this.getHeaderCell(index).firstChild;
53702     },
53703
53704     getHeaderCellText : function(index){
53705         return this.getHeaderCell(index).firstChild.firstChild;
53706     },
53707
53708     getLockedTable : function(){
53709         return this.lockedBody.dom.firstChild;
53710     },
53711
53712     getBodyTable : function(){
53713         return this.mainBody.dom.firstChild;
53714     },
53715
53716     getLockedRow : function(index){
53717         return this.getLockedTable().rows[index];
53718     },
53719
53720     getRow : function(index){
53721         return this.getBodyTable().rows[index];
53722     },
53723
53724     getRowComposite : function(index){
53725         if(!this.rowEl){
53726             this.rowEl = new Roo.CompositeElementLite();
53727         }
53728         var els = [], lrow, mrow;
53729         if(lrow = this.getLockedRow(index)){
53730             els.push(lrow);
53731         }
53732         if(mrow = this.getRow(index)){
53733             els.push(mrow);
53734         }
53735         this.rowEl.elements = els;
53736         return this.rowEl;
53737     },
53738     /**
53739      * Gets the 'td' of the cell
53740      * 
53741      * @param {Integer} rowIndex row to select
53742      * @param {Integer} colIndex column to select
53743      * 
53744      * @return {Object} 
53745      */
53746     getCell : function(rowIndex, colIndex){
53747         var locked = this.cm.getLockedCount();
53748         var source;
53749         if(colIndex < locked){
53750             source = this.lockedBody.dom.firstChild;
53751         }else{
53752             source = this.mainBody.dom.firstChild;
53753             colIndex -= locked;
53754         }
53755         return source.rows[rowIndex].childNodes[colIndex];
53756     },
53757
53758     getCellText : function(rowIndex, colIndex){
53759         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53760     },
53761
53762     getCellBox : function(cell){
53763         var b = this.fly(cell).getBox();
53764         if(Roo.isOpera){ // opera fails to report the Y
53765             b.y = cell.offsetTop + this.mainBody.getY();
53766         }
53767         return b;
53768     },
53769
53770     getCellIndex : function(cell){
53771         var id = String(cell.className).match(this.cellRE);
53772         if(id){
53773             return parseInt(id[1], 10);
53774         }
53775         return 0;
53776     },
53777
53778     findHeaderIndex : function(n){
53779         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53780         return r ? this.getCellIndex(r) : false;
53781     },
53782
53783     findHeaderCell : function(n){
53784         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53785         return r ? r : false;
53786     },
53787
53788     findRowIndex : function(n){
53789         if(!n){
53790             return false;
53791         }
53792         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53793         return r ? r.rowIndex : false;
53794     },
53795
53796     findCellIndex : function(node){
53797         var stop = this.el.dom;
53798         while(node && node != stop){
53799             if(this.findRE.test(node.className)){
53800                 return this.getCellIndex(node);
53801             }
53802             node = node.parentNode;
53803         }
53804         return false;
53805     },
53806
53807     getColumnId : function(index){
53808         return this.cm.getColumnId(index);
53809     },
53810
53811     getSplitters : function()
53812     {
53813         if(this.splitterSelector){
53814            return Roo.DomQuery.select(this.splitterSelector);
53815         }else{
53816             return null;
53817       }
53818     },
53819
53820     getSplitter : function(index){
53821         return this.getSplitters()[index];
53822     },
53823
53824     onRowOver : function(e, t){
53825         var row;
53826         if((row = this.findRowIndex(t)) !== false){
53827             this.getRowComposite(row).addClass("x-grid-row-over");
53828         }
53829     },
53830
53831     onRowOut : function(e, t){
53832         var row;
53833         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53834             this.getRowComposite(row).removeClass("x-grid-row-over");
53835         }
53836     },
53837
53838     renderHeaders : function(){
53839         var cm = this.cm;
53840         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53841         var cb = [], lb = [], sb = [], lsb = [], p = {};
53842         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53843             p.cellId = "x-grid-hd-0-" + i;
53844             p.splitId = "x-grid-csplit-0-" + i;
53845             p.id = cm.getColumnId(i);
53846             p.title = cm.getColumnTooltip(i) || "";
53847             p.value = cm.getColumnHeader(i) || "";
53848             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53849             if(!cm.isLocked(i)){
53850                 cb[cb.length] = ct.apply(p);
53851                 sb[sb.length] = st.apply(p);
53852             }else{
53853                 lb[lb.length] = ct.apply(p);
53854                 lsb[lsb.length] = st.apply(p);
53855             }
53856         }
53857         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53858                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53859     },
53860
53861     updateHeaders : function(){
53862         var html = this.renderHeaders();
53863         this.lockedHd.update(html[0]);
53864         this.mainHd.update(html[1]);
53865     },
53866
53867     /**
53868      * Focuses the specified row.
53869      * @param {Number} row The row index
53870      */
53871     focusRow : function(row)
53872     {
53873         //Roo.log('GridView.focusRow');
53874         var x = this.scroller.dom.scrollLeft;
53875         this.focusCell(row, 0, false);
53876         this.scroller.dom.scrollLeft = x;
53877     },
53878
53879     /**
53880      * Focuses the specified cell.
53881      * @param {Number} row The row index
53882      * @param {Number} col The column index
53883      * @param {Boolean} hscroll false to disable horizontal scrolling
53884      */
53885     focusCell : function(row, col, hscroll)
53886     {
53887         //Roo.log('GridView.focusCell');
53888         var el = this.ensureVisible(row, col, hscroll);
53889         this.focusEl.alignTo(el, "tl-tl");
53890         if(Roo.isGecko){
53891             this.focusEl.focus();
53892         }else{
53893             this.focusEl.focus.defer(1, this.focusEl);
53894         }
53895     },
53896
53897     /**
53898      * Scrolls the specified cell into view
53899      * @param {Number} row The row index
53900      * @param {Number} col The column index
53901      * @param {Boolean} hscroll false to disable horizontal scrolling
53902      */
53903     ensureVisible : function(row, col, hscroll)
53904     {
53905         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53906         //return null; //disable for testing.
53907         if(typeof row != "number"){
53908             row = row.rowIndex;
53909         }
53910         if(row < 0 && row >= this.ds.getCount()){
53911             return  null;
53912         }
53913         col = (col !== undefined ? col : 0);
53914         var cm = this.grid.colModel;
53915         while(cm.isHidden(col)){
53916             col++;
53917         }
53918
53919         var el = this.getCell(row, col);
53920         if(!el){
53921             return null;
53922         }
53923         var c = this.scroller.dom;
53924
53925         var ctop = parseInt(el.offsetTop, 10);
53926         var cleft = parseInt(el.offsetLeft, 10);
53927         var cbot = ctop + el.offsetHeight;
53928         var cright = cleft + el.offsetWidth;
53929         
53930         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53931         var stop = parseInt(c.scrollTop, 10);
53932         var sleft = parseInt(c.scrollLeft, 10);
53933         var sbot = stop + ch;
53934         var sright = sleft + c.clientWidth;
53935         /*
53936         Roo.log('GridView.ensureVisible:' +
53937                 ' ctop:' + ctop +
53938                 ' c.clientHeight:' + c.clientHeight +
53939                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53940                 ' stop:' + stop +
53941                 ' cbot:' + cbot +
53942                 ' sbot:' + sbot +
53943                 ' ch:' + ch  
53944                 );
53945         */
53946         if(ctop < stop){
53947              c.scrollTop = ctop;
53948             //Roo.log("set scrolltop to ctop DISABLE?");
53949         }else if(cbot > sbot){
53950             //Roo.log("set scrolltop to cbot-ch");
53951             c.scrollTop = cbot-ch;
53952         }
53953         
53954         if(hscroll !== false){
53955             if(cleft < sleft){
53956                 c.scrollLeft = cleft;
53957             }else if(cright > sright){
53958                 c.scrollLeft = cright-c.clientWidth;
53959             }
53960         }
53961          
53962         return el;
53963     },
53964
53965     updateColumns : function(){
53966         this.grid.stopEditing();
53967         var cm = this.grid.colModel, colIds = this.getColumnIds();
53968         //var totalWidth = cm.getTotalWidth();
53969         var pos = 0;
53970         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53971             //if(cm.isHidden(i)) continue;
53972             var w = cm.getColumnWidth(i);
53973             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53974             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53975         }
53976         this.updateSplitters();
53977     },
53978
53979     generateRules : function(cm){
53980         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53981         Roo.util.CSS.removeStyleSheet(rulesId);
53982         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53983             var cid = cm.getColumnId(i);
53984             var align = '';
53985             if(cm.config[i].align){
53986                 align = 'text-align:'+cm.config[i].align+';';
53987             }
53988             var hidden = '';
53989             if(cm.isHidden(i)){
53990                 hidden = 'display:none;';
53991             }
53992             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53993             ruleBuf.push(
53994                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53995                     this.hdSelector, cid, " {\n", align, width, "}\n",
53996                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53997                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53998         }
53999         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54000     },
54001
54002     updateSplitters : function(){
54003         var cm = this.cm, s = this.getSplitters();
54004         if(s){ // splitters not created yet
54005             var pos = 0, locked = true;
54006             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54007                 if(cm.isHidden(i)) continue;
54008                 var w = cm.getColumnWidth(i); // make sure it's a number
54009                 if(!cm.isLocked(i) && locked){
54010                     pos = 0;
54011                     locked = false;
54012                 }
54013                 pos += w;
54014                 s[i].style.left = (pos-this.splitOffset) + "px";
54015             }
54016         }
54017     },
54018
54019     handleHiddenChange : function(colModel, colIndex, hidden){
54020         if(hidden){
54021             this.hideColumn(colIndex);
54022         }else{
54023             this.unhideColumn(colIndex);
54024         }
54025     },
54026
54027     hideColumn : function(colIndex){
54028         var cid = this.getColumnId(colIndex);
54029         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54030         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54031         if(Roo.isSafari){
54032             this.updateHeaders();
54033         }
54034         this.updateSplitters();
54035         this.layout();
54036     },
54037
54038     unhideColumn : function(colIndex){
54039         var cid = this.getColumnId(colIndex);
54040         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54041         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54042
54043         if(Roo.isSafari){
54044             this.updateHeaders();
54045         }
54046         this.updateSplitters();
54047         this.layout();
54048     },
54049
54050     insertRows : function(dm, firstRow, lastRow, isUpdate){
54051         if(firstRow == 0 && lastRow == dm.getCount()-1){
54052             this.refresh();
54053         }else{
54054             if(!isUpdate){
54055                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54056             }
54057             var s = this.getScrollState();
54058             var markup = this.renderRows(firstRow, lastRow);
54059             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54060             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54061             this.restoreScroll(s);
54062             if(!isUpdate){
54063                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54064                 this.syncRowHeights(firstRow, lastRow);
54065                 this.stripeRows(firstRow);
54066                 this.layout();
54067             }
54068         }
54069     },
54070
54071     bufferRows : function(markup, target, index){
54072         var before = null, trows = target.rows, tbody = target.tBodies[0];
54073         if(index < trows.length){
54074             before = trows[index];
54075         }
54076         var b = document.createElement("div");
54077         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54078         var rows = b.firstChild.rows;
54079         for(var i = 0, len = rows.length; i < len; i++){
54080             if(before){
54081                 tbody.insertBefore(rows[0], before);
54082             }else{
54083                 tbody.appendChild(rows[0]);
54084             }
54085         }
54086         b.innerHTML = "";
54087         b = null;
54088     },
54089
54090     deleteRows : function(dm, firstRow, lastRow){
54091         if(dm.getRowCount()<1){
54092             this.fireEvent("beforerefresh", this);
54093             this.mainBody.update("");
54094             this.lockedBody.update("");
54095             this.fireEvent("refresh", this);
54096         }else{
54097             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54098             var bt = this.getBodyTable();
54099             var tbody = bt.firstChild;
54100             var rows = bt.rows;
54101             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54102                 tbody.removeChild(rows[firstRow]);
54103             }
54104             this.stripeRows(firstRow);
54105             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54106         }
54107     },
54108
54109     updateRows : function(dataSource, firstRow, lastRow){
54110         var s = this.getScrollState();
54111         this.refresh();
54112         this.restoreScroll(s);
54113     },
54114
54115     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54116         if(!noRefresh){
54117            this.refresh();
54118         }
54119         this.updateHeaderSortState();
54120     },
54121
54122     getScrollState : function(){
54123         
54124         var sb = this.scroller.dom;
54125         return {left: sb.scrollLeft, top: sb.scrollTop};
54126     },
54127
54128     stripeRows : function(startRow){
54129         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54130             return;
54131         }
54132         startRow = startRow || 0;
54133         var rows = this.getBodyTable().rows;
54134         var lrows = this.getLockedTable().rows;
54135         var cls = ' x-grid-row-alt ';
54136         for(var i = startRow, len = rows.length; i < len; i++){
54137             var row = rows[i], lrow = lrows[i];
54138             var isAlt = ((i+1) % 2 == 0);
54139             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54140             if(isAlt == hasAlt){
54141                 continue;
54142             }
54143             if(isAlt){
54144                 row.className += " x-grid-row-alt";
54145             }else{
54146                 row.className = row.className.replace("x-grid-row-alt", "");
54147             }
54148             if(lrow){
54149                 lrow.className = row.className;
54150             }
54151         }
54152     },
54153
54154     restoreScroll : function(state){
54155         //Roo.log('GridView.restoreScroll');
54156         var sb = this.scroller.dom;
54157         sb.scrollLeft = state.left;
54158         sb.scrollTop = state.top;
54159         this.syncScroll();
54160     },
54161
54162     syncScroll : function(){
54163         //Roo.log('GridView.syncScroll');
54164         var sb = this.scroller.dom;
54165         var sh = this.mainHd.dom;
54166         var bs = this.mainBody.dom;
54167         var lv = this.lockedBody.dom;
54168         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54169         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54170     },
54171
54172     handleScroll : function(e){
54173         this.syncScroll();
54174         var sb = this.scroller.dom;
54175         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54176         e.stopEvent();
54177     },
54178
54179     handleWheel : function(e){
54180         var d = e.getWheelDelta();
54181         this.scroller.dom.scrollTop -= d*22;
54182         // set this here to prevent jumpy scrolling on large tables
54183         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54184         e.stopEvent();
54185     },
54186
54187     renderRows : function(startRow, endRow){
54188         // pull in all the crap needed to render rows
54189         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54190         var colCount = cm.getColumnCount();
54191
54192         if(ds.getCount() < 1){
54193             return ["", ""];
54194         }
54195
54196         // build a map for all the columns
54197         var cs = [];
54198         for(var i = 0; i < colCount; i++){
54199             var name = cm.getDataIndex(i);
54200             cs[i] = {
54201                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54202                 renderer : cm.getRenderer(i),
54203                 id : cm.getColumnId(i),
54204                 locked : cm.isLocked(i)
54205             };
54206         }
54207
54208         startRow = startRow || 0;
54209         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54210
54211         // records to render
54212         var rs = ds.getRange(startRow, endRow);
54213
54214         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54215     },
54216
54217     // As much as I hate to duplicate code, this was branched because FireFox really hates
54218     // [].join("") on strings. The performance difference was substantial enough to
54219     // branch this function
54220     doRender : Roo.isGecko ?
54221             function(cs, rs, ds, startRow, colCount, stripe){
54222                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54223                 // buffers
54224                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54225                 
54226                 var hasListener = this.grid.hasListener('rowclass');
54227                 var rowcfg = {};
54228                 for(var j = 0, len = rs.length; j < len; j++){
54229                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54230                     for(var i = 0; i < colCount; i++){
54231                         c = cs[i];
54232                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54233                         p.id = c.id;
54234                         p.css = p.attr = "";
54235                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54236                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54237                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54238                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54239                         }
54240                         var markup = ct.apply(p);
54241                         if(!c.locked){
54242                             cb+= markup;
54243                         }else{
54244                             lcb+= markup;
54245                         }
54246                     }
54247                     var alt = [];
54248                     if(stripe && ((rowIndex+1) % 2 == 0)){
54249                         alt.push("x-grid-row-alt")
54250                     }
54251                     if(r.dirty){
54252                         alt.push(  " x-grid-dirty-row");
54253                     }
54254                     rp.cells = lcb;
54255                     if(this.getRowClass){
54256                         alt.push(this.getRowClass(r, rowIndex));
54257                     }
54258                     if (hasListener) {
54259                         rowcfg = {
54260                              
54261                             record: r,
54262                             rowIndex : rowIndex,
54263                             rowClass : ''
54264                         }
54265                         this.grid.fireEvent('rowclass', this, rowcfg);
54266                         alt.push(rowcfg.rowClass);
54267                     }
54268                     rp.alt = alt.join(" ");
54269                     lbuf+= rt.apply(rp);
54270                     rp.cells = cb;
54271                     buf+=  rt.apply(rp);
54272                 }
54273                 return [lbuf, buf];
54274             } :
54275             function(cs, rs, ds, startRow, colCount, stripe){
54276                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54277                 // buffers
54278                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54279                 var hasListener = this.grid.hasListener('rowclass');
54280  
54281                 var rowcfg = {};
54282                 for(var j = 0, len = rs.length; j < len; j++){
54283                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54284                     for(var i = 0; i < colCount; i++){
54285                         c = cs[i];
54286                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54287                         p.id = c.id;
54288                         p.css = p.attr = "";
54289                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54290                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54291                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54292                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54293                         }
54294                         
54295                         var markup = ct.apply(p);
54296                         if(!c.locked){
54297                             cb[cb.length] = markup;
54298                         }else{
54299                             lcb[lcb.length] = markup;
54300                         }
54301                     }
54302                     var alt = [];
54303                     if(stripe && ((rowIndex+1) % 2 == 0)){
54304                         alt.push( "x-grid-row-alt");
54305                     }
54306                     if(r.dirty){
54307                         alt.push(" x-grid-dirty-row");
54308                     }
54309                     rp.cells = lcb;
54310                     if(this.getRowClass){
54311                         alt.push( this.getRowClass(r, rowIndex));
54312                     }
54313                     if (hasListener) {
54314                         rowcfg = {
54315                              
54316                             record: r,
54317                             rowIndex : rowIndex,
54318                             rowClass : ''
54319                         }
54320                         this.grid.fireEvent('rowclass', this, rowcfg);
54321                         alt.push(rowcfg.rowClass);
54322                     }
54323                     rp.alt = alt.join(" ");
54324                     rp.cells = lcb.join("");
54325                     lbuf[lbuf.length] = rt.apply(rp);
54326                     rp.cells = cb.join("");
54327                     buf[buf.length] =  rt.apply(rp);
54328                 }
54329                 return [lbuf.join(""), buf.join("")];
54330             },
54331
54332     renderBody : function(){
54333         var markup = this.renderRows();
54334         var bt = this.templates.body;
54335         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54336     },
54337
54338     /**
54339      * Refreshes the grid
54340      * @param {Boolean} headersToo
54341      */
54342     refresh : function(headersToo){
54343         this.fireEvent("beforerefresh", this);
54344         this.grid.stopEditing();
54345         var result = this.renderBody();
54346         this.lockedBody.update(result[0]);
54347         this.mainBody.update(result[1]);
54348         if(headersToo === true){
54349             this.updateHeaders();
54350             this.updateColumns();
54351             this.updateSplitters();
54352             this.updateHeaderSortState();
54353         }
54354         this.syncRowHeights();
54355         this.layout();
54356         this.fireEvent("refresh", this);
54357     },
54358
54359     handleColumnMove : function(cm, oldIndex, newIndex){
54360         this.indexMap = null;
54361         var s = this.getScrollState();
54362         this.refresh(true);
54363         this.restoreScroll(s);
54364         this.afterMove(newIndex);
54365     },
54366
54367     afterMove : function(colIndex){
54368         if(this.enableMoveAnim && Roo.enableFx){
54369             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54370         }
54371         // if multisort - fix sortOrder, and reload..
54372         if (this.grid.dataSource.multiSort) {
54373             // the we can call sort again..
54374             var dm = this.grid.dataSource;
54375             var cm = this.grid.colModel;
54376             var so = [];
54377             for(var i = 0; i < cm.config.length; i++ ) {
54378                 
54379                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54380                     continue; // dont' bother, it's not in sort list or being set.
54381                 }
54382                 
54383                 so.push(cm.config[i].dataIndex);
54384             };
54385             dm.sortOrder = so;
54386             dm.load(dm.lastOptions);
54387             
54388             
54389         }
54390         
54391     },
54392
54393     updateCell : function(dm, rowIndex, dataIndex){
54394         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54395         if(typeof colIndex == "undefined"){ // not present in grid
54396             return;
54397         }
54398         var cm = this.grid.colModel;
54399         var cell = this.getCell(rowIndex, colIndex);
54400         var cellText = this.getCellText(rowIndex, colIndex);
54401
54402         var p = {
54403             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54404             id : cm.getColumnId(colIndex),
54405             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54406         };
54407         var renderer = cm.getRenderer(colIndex);
54408         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54409         if(typeof val == "undefined" || val === "") val = "&#160;";
54410         cellText.innerHTML = val;
54411         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54412         this.syncRowHeights(rowIndex, rowIndex);
54413     },
54414
54415     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54416         var maxWidth = 0;
54417         if(this.grid.autoSizeHeaders){
54418             var h = this.getHeaderCellMeasure(colIndex);
54419             maxWidth = Math.max(maxWidth, h.scrollWidth);
54420         }
54421         var tb, index;
54422         if(this.cm.isLocked(colIndex)){
54423             tb = this.getLockedTable();
54424             index = colIndex;
54425         }else{
54426             tb = this.getBodyTable();
54427             index = colIndex - this.cm.getLockedCount();
54428         }
54429         if(tb && tb.rows){
54430             var rows = tb.rows;
54431             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54432             for(var i = 0; i < stopIndex; i++){
54433                 var cell = rows[i].childNodes[index].firstChild;
54434                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54435             }
54436         }
54437         return maxWidth + /*margin for error in IE*/ 5;
54438     },
54439     /**
54440      * Autofit a column to its content.
54441      * @param {Number} colIndex
54442      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54443      */
54444      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54445          if(this.cm.isHidden(colIndex)){
54446              return; // can't calc a hidden column
54447          }
54448         if(forceMinSize){
54449             var cid = this.cm.getColumnId(colIndex);
54450             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54451            if(this.grid.autoSizeHeaders){
54452                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54453            }
54454         }
54455         var newWidth = this.calcColumnWidth(colIndex);
54456         this.cm.setColumnWidth(colIndex,
54457             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54458         if(!suppressEvent){
54459             this.grid.fireEvent("columnresize", colIndex, newWidth);
54460         }
54461     },
54462
54463     /**
54464      * Autofits all columns to their content and then expands to fit any extra space in the grid
54465      */
54466      autoSizeColumns : function(){
54467         var cm = this.grid.colModel;
54468         var colCount = cm.getColumnCount();
54469         for(var i = 0; i < colCount; i++){
54470             this.autoSizeColumn(i, true, true);
54471         }
54472         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54473             this.fitColumns();
54474         }else{
54475             this.updateColumns();
54476             this.layout();
54477         }
54478     },
54479
54480     /**
54481      * Autofits all columns to the grid's width proportionate with their current size
54482      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54483      */
54484     fitColumns : function(reserveScrollSpace){
54485         var cm = this.grid.colModel;
54486         var colCount = cm.getColumnCount();
54487         var cols = [];
54488         var width = 0;
54489         var i, w;
54490         for (i = 0; i < colCount; i++){
54491             if(!cm.isHidden(i) && !cm.isFixed(i)){
54492                 w = cm.getColumnWidth(i);
54493                 cols.push(i);
54494                 cols.push(w);
54495                 width += w;
54496             }
54497         }
54498         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54499         if(reserveScrollSpace){
54500             avail -= 17;
54501         }
54502         var frac = (avail - cm.getTotalWidth())/width;
54503         while (cols.length){
54504             w = cols.pop();
54505             i = cols.pop();
54506             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54507         }
54508         this.updateColumns();
54509         this.layout();
54510     },
54511
54512     onRowSelect : function(rowIndex){
54513         var row = this.getRowComposite(rowIndex);
54514         row.addClass("x-grid-row-selected");
54515     },
54516
54517     onRowDeselect : function(rowIndex){
54518         var row = this.getRowComposite(rowIndex);
54519         row.removeClass("x-grid-row-selected");
54520     },
54521
54522     onCellSelect : function(row, col){
54523         var cell = this.getCell(row, col);
54524         if(cell){
54525             Roo.fly(cell).addClass("x-grid-cell-selected");
54526         }
54527     },
54528
54529     onCellDeselect : function(row, col){
54530         var cell = this.getCell(row, col);
54531         if(cell){
54532             Roo.fly(cell).removeClass("x-grid-cell-selected");
54533         }
54534     },
54535
54536     updateHeaderSortState : function(){
54537         
54538         // sort state can be single { field: xxx, direction : yyy}
54539         // or   { xxx=>ASC , yyy : DESC ..... }
54540         
54541         var mstate = {};
54542         if (!this.ds.multiSort) { 
54543             var state = this.ds.getSortState();
54544             if(!state){
54545                 return;
54546             }
54547             mstate[state.field] = state.direction;
54548             // FIXME... - this is not used here.. but might be elsewhere..
54549             this.sortState = state;
54550             
54551         } else {
54552             mstate = this.ds.sortToggle;
54553         }
54554         //remove existing sort classes..
54555         
54556         var sc = this.sortClasses;
54557         var hds = this.el.select(this.headerSelector).removeClass(sc);
54558         
54559         for(var f in mstate) {
54560         
54561             var sortColumn = this.cm.findColumnIndex(f);
54562             
54563             if(sortColumn != -1){
54564                 var sortDir = mstate[f];        
54565                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54566             }
54567         }
54568         
54569          
54570         
54571     },
54572
54573
54574     handleHeaderClick : function(g, index,e){
54575         
54576         Roo.log("header click");
54577         
54578         if (Roo.isTouch) {
54579             // touch events on header are handled by context
54580             this.handleHdCtx(g,index,e);
54581             return;
54582         }
54583         
54584         
54585         if(this.headersDisabled){
54586             return;
54587         }
54588         var dm = g.dataSource, cm = g.colModel;
54589         if(!cm.isSortable(index)){
54590             return;
54591         }
54592         g.stopEditing();
54593         
54594         if (dm.multiSort) {
54595             // update the sortOrder
54596             var so = [];
54597             for(var i = 0; i < cm.config.length; i++ ) {
54598                 
54599                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54600                     continue; // dont' bother, it's not in sort list or being set.
54601                 }
54602                 
54603                 so.push(cm.config[i].dataIndex);
54604             };
54605             dm.sortOrder = so;
54606         }
54607         
54608         
54609         dm.sort(cm.getDataIndex(index));
54610     },
54611
54612
54613     destroy : function(){
54614         if(this.colMenu){
54615             this.colMenu.removeAll();
54616             Roo.menu.MenuMgr.unregister(this.colMenu);
54617             this.colMenu.getEl().remove();
54618             delete this.colMenu;
54619         }
54620         if(this.hmenu){
54621             this.hmenu.removeAll();
54622             Roo.menu.MenuMgr.unregister(this.hmenu);
54623             this.hmenu.getEl().remove();
54624             delete this.hmenu;
54625         }
54626         if(this.grid.enableColumnMove){
54627             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54628             if(dds){
54629                 for(var dd in dds){
54630                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54631                         var elid = dds[dd].dragElId;
54632                         dds[dd].unreg();
54633                         Roo.get(elid).remove();
54634                     } else if(dds[dd].config.isTarget){
54635                         dds[dd].proxyTop.remove();
54636                         dds[dd].proxyBottom.remove();
54637                         dds[dd].unreg();
54638                     }
54639                     if(Roo.dd.DDM.locationCache[dd]){
54640                         delete Roo.dd.DDM.locationCache[dd];
54641                     }
54642                 }
54643                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54644             }
54645         }
54646         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54647         this.bind(null, null);
54648         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54649     },
54650
54651     handleLockChange : function(){
54652         this.refresh(true);
54653     },
54654
54655     onDenyColumnLock : function(){
54656
54657     },
54658
54659     onDenyColumnHide : function(){
54660
54661     },
54662
54663     handleHdMenuClick : function(item){
54664         var index = this.hdCtxIndex;
54665         var cm = this.cm, ds = this.ds;
54666         switch(item.id){
54667             case "asc":
54668                 ds.sort(cm.getDataIndex(index), "ASC");
54669                 break;
54670             case "desc":
54671                 ds.sort(cm.getDataIndex(index), "DESC");
54672                 break;
54673             case "lock":
54674                 var lc = cm.getLockedCount();
54675                 if(cm.getColumnCount(true) <= lc+1){
54676                     this.onDenyColumnLock();
54677                     return;
54678                 }
54679                 if(lc != index){
54680                     cm.setLocked(index, true, true);
54681                     cm.moveColumn(index, lc);
54682                     this.grid.fireEvent("columnmove", index, lc);
54683                 }else{
54684                     cm.setLocked(index, true);
54685                 }
54686             break;
54687             case "unlock":
54688                 var lc = cm.getLockedCount();
54689                 if((lc-1) != index){
54690                     cm.setLocked(index, false, true);
54691                     cm.moveColumn(index, lc-1);
54692                     this.grid.fireEvent("columnmove", index, lc-1);
54693                 }else{
54694                     cm.setLocked(index, false);
54695                 }
54696             break;
54697             case 'wider': // used to expand cols on touch..
54698             case 'narrow':
54699                 var cw = cm.getColumnWidth(index);
54700                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54701                 cw = Math.max(0, cw);
54702                 cw = Math.min(cw,4000);
54703                 cm.setColumnWidth(index, cw);
54704                 break;
54705                 
54706             default:
54707                 index = cm.getIndexById(item.id.substr(4));
54708                 if(index != -1){
54709                     if(item.checked && cm.getColumnCount(true) <= 1){
54710                         this.onDenyColumnHide();
54711                         return false;
54712                     }
54713                     cm.setHidden(index, item.checked);
54714                 }
54715         }
54716         return true;
54717     },
54718
54719     beforeColMenuShow : function(){
54720         var cm = this.cm,  colCount = cm.getColumnCount();
54721         this.colMenu.removeAll();
54722         for(var i = 0; i < colCount; i++){
54723             this.colMenu.add(new Roo.menu.CheckItem({
54724                 id: "col-"+cm.getColumnId(i),
54725                 text: cm.getColumnHeader(i),
54726                 checked: !cm.isHidden(i),
54727                 hideOnClick:false
54728             }));
54729         }
54730     },
54731
54732     handleHdCtx : function(g, index, e){
54733         e.stopEvent();
54734         var hd = this.getHeaderCell(index);
54735         this.hdCtxIndex = index;
54736         var ms = this.hmenu.items, cm = this.cm;
54737         ms.get("asc").setDisabled(!cm.isSortable(index));
54738         ms.get("desc").setDisabled(!cm.isSortable(index));
54739         if(this.grid.enableColLock !== false){
54740             ms.get("lock").setDisabled(cm.isLocked(index));
54741             ms.get("unlock").setDisabled(!cm.isLocked(index));
54742         }
54743         this.hmenu.show(hd, "tl-bl");
54744     },
54745
54746     handleHdOver : function(e){
54747         var hd = this.findHeaderCell(e.getTarget());
54748         if(hd && !this.headersDisabled){
54749             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54750                this.fly(hd).addClass("x-grid-hd-over");
54751             }
54752         }
54753     },
54754
54755     handleHdOut : function(e){
54756         var hd = this.findHeaderCell(e.getTarget());
54757         if(hd){
54758             this.fly(hd).removeClass("x-grid-hd-over");
54759         }
54760     },
54761
54762     handleSplitDblClick : function(e, t){
54763         var i = this.getCellIndex(t);
54764         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54765             this.autoSizeColumn(i, true);
54766             this.layout();
54767         }
54768     },
54769
54770     render : function(){
54771
54772         var cm = this.cm;
54773         var colCount = cm.getColumnCount();
54774
54775         if(this.grid.monitorWindowResize === true){
54776             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54777         }
54778         var header = this.renderHeaders();
54779         var body = this.templates.body.apply({rows:""});
54780         var html = this.templates.master.apply({
54781             lockedBody: body,
54782             body: body,
54783             lockedHeader: header[0],
54784             header: header[1]
54785         });
54786
54787         //this.updateColumns();
54788
54789         this.grid.getGridEl().dom.innerHTML = html;
54790
54791         this.initElements();
54792         
54793         // a kludge to fix the random scolling effect in webkit
54794         this.el.on("scroll", function() {
54795             this.el.dom.scrollTop=0; // hopefully not recursive..
54796         },this);
54797
54798         this.scroller.on("scroll", this.handleScroll, this);
54799         this.lockedBody.on("mousewheel", this.handleWheel, this);
54800         this.mainBody.on("mousewheel", this.handleWheel, this);
54801
54802         this.mainHd.on("mouseover", this.handleHdOver, this);
54803         this.mainHd.on("mouseout", this.handleHdOut, this);
54804         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54805                 {delegate: "."+this.splitClass});
54806
54807         this.lockedHd.on("mouseover", this.handleHdOver, this);
54808         this.lockedHd.on("mouseout", this.handleHdOut, this);
54809         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54810                 {delegate: "."+this.splitClass});
54811
54812         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54813             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54814         }
54815
54816         this.updateSplitters();
54817
54818         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54819             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54820             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54821         }
54822
54823         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54824             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54825             this.hmenu.add(
54826                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54827                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54828             );
54829             if(this.grid.enableColLock !== false){
54830                 this.hmenu.add('-',
54831                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54832                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54833                 );
54834             }
54835             if (Roo.isTouch) {
54836                  this.hmenu.add('-',
54837                     {id:"wider", text: this.columnsWiderText},
54838                     {id:"narrow", text: this.columnsNarrowText }
54839                 );
54840                 
54841                  
54842             }
54843             
54844             if(this.grid.enableColumnHide !== false){
54845
54846                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54847                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54848                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54849
54850                 this.hmenu.add('-',
54851                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54852                 );
54853             }
54854             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54855
54856             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54857         }
54858
54859         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54860             this.dd = new Roo.grid.GridDragZone(this.grid, {
54861                 ddGroup : this.grid.ddGroup || 'GridDD'
54862             });
54863             
54864         }
54865
54866         /*
54867         for(var i = 0; i < colCount; i++){
54868             if(cm.isHidden(i)){
54869                 this.hideColumn(i);
54870             }
54871             if(cm.config[i].align){
54872                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54873                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54874             }
54875         }*/
54876         
54877         this.updateHeaderSortState();
54878
54879         this.beforeInitialResize();
54880         this.layout(true);
54881
54882         // two part rendering gives faster view to the user
54883         this.renderPhase2.defer(1, this);
54884     },
54885
54886     renderPhase2 : function(){
54887         // render the rows now
54888         this.refresh();
54889         if(this.grid.autoSizeColumns){
54890             this.autoSizeColumns();
54891         }
54892     },
54893
54894     beforeInitialResize : function(){
54895
54896     },
54897
54898     onColumnSplitterMoved : function(i, w){
54899         this.userResized = true;
54900         var cm = this.grid.colModel;
54901         cm.setColumnWidth(i, w, true);
54902         var cid = cm.getColumnId(i);
54903         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54904         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54905         this.updateSplitters();
54906         this.layout();
54907         this.grid.fireEvent("columnresize", i, w);
54908     },
54909
54910     syncRowHeights : function(startIndex, endIndex){
54911         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54912             startIndex = startIndex || 0;
54913             var mrows = this.getBodyTable().rows;
54914             var lrows = this.getLockedTable().rows;
54915             var len = mrows.length-1;
54916             endIndex = Math.min(endIndex || len, len);
54917             for(var i = startIndex; i <= endIndex; i++){
54918                 var m = mrows[i], l = lrows[i];
54919                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54920                 m.style.height = l.style.height = h + "px";
54921             }
54922         }
54923     },
54924
54925     layout : function(initialRender, is2ndPass){
54926         var g = this.grid;
54927         var auto = g.autoHeight;
54928         var scrollOffset = 16;
54929         var c = g.getGridEl(), cm = this.cm,
54930                 expandCol = g.autoExpandColumn,
54931                 gv = this;
54932         //c.beginMeasure();
54933
54934         if(!c.dom.offsetWidth){ // display:none?
54935             if(initialRender){
54936                 this.lockedWrap.show();
54937                 this.mainWrap.show();
54938             }
54939             return;
54940         }
54941
54942         var hasLock = this.cm.isLocked(0);
54943
54944         var tbh = this.headerPanel.getHeight();
54945         var bbh = this.footerPanel.getHeight();
54946
54947         if(auto){
54948             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54949             var newHeight = ch + c.getBorderWidth("tb");
54950             if(g.maxHeight){
54951                 newHeight = Math.min(g.maxHeight, newHeight);
54952             }
54953             c.setHeight(newHeight);
54954         }
54955
54956         if(g.autoWidth){
54957             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54958         }
54959
54960         var s = this.scroller;
54961
54962         var csize = c.getSize(true);
54963
54964         this.el.setSize(csize.width, csize.height);
54965
54966         this.headerPanel.setWidth(csize.width);
54967         this.footerPanel.setWidth(csize.width);
54968
54969         var hdHeight = this.mainHd.getHeight();
54970         var vw = csize.width;
54971         var vh = csize.height - (tbh + bbh);
54972
54973         s.setSize(vw, vh);
54974
54975         var bt = this.getBodyTable();
54976         var ltWidth = hasLock ?
54977                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54978
54979         var scrollHeight = bt.offsetHeight;
54980         var scrollWidth = ltWidth + bt.offsetWidth;
54981         var vscroll = false, hscroll = false;
54982
54983         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54984
54985         var lw = this.lockedWrap, mw = this.mainWrap;
54986         var lb = this.lockedBody, mb = this.mainBody;
54987
54988         setTimeout(function(){
54989             var t = s.dom.offsetTop;
54990             var w = s.dom.clientWidth,
54991                 h = s.dom.clientHeight;
54992
54993             lw.setTop(t);
54994             lw.setSize(ltWidth, h);
54995
54996             mw.setLeftTop(ltWidth, t);
54997             mw.setSize(w-ltWidth, h);
54998
54999             lb.setHeight(h-hdHeight);
55000             mb.setHeight(h-hdHeight);
55001
55002             if(is2ndPass !== true && !gv.userResized && expandCol){
55003                 // high speed resize without full column calculation
55004                 
55005                 var ci = cm.getIndexById(expandCol);
55006                 if (ci < 0) {
55007                     ci = cm.findColumnIndex(expandCol);
55008                 }
55009                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55010                 var expandId = cm.getColumnId(ci);
55011                 var  tw = cm.getTotalWidth(false);
55012                 var currentWidth = cm.getColumnWidth(ci);
55013                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55014                 if(currentWidth != cw){
55015                     cm.setColumnWidth(ci, cw, true);
55016                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55017                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55018                     gv.updateSplitters();
55019                     gv.layout(false, true);
55020                 }
55021             }
55022
55023             if(initialRender){
55024                 lw.show();
55025                 mw.show();
55026             }
55027             //c.endMeasure();
55028         }, 10);
55029     },
55030
55031     onWindowResize : function(){
55032         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55033             return;
55034         }
55035         this.layout();
55036     },
55037
55038     appendFooter : function(parentEl){
55039         return null;
55040     },
55041
55042     sortAscText : "Sort Ascending",
55043     sortDescText : "Sort Descending",
55044     lockText : "Lock Column",
55045     unlockText : "Unlock Column",
55046     columnsText : "Columns",
55047  
55048     columnsWiderText : "Wider",
55049     columnsNarrowText : "Thinner"
55050 });
55051
55052
55053 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55054     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55055     this.proxy.el.addClass('x-grid3-col-dd');
55056 };
55057
55058 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55059     handleMouseDown : function(e){
55060
55061     },
55062
55063     callHandleMouseDown : function(e){
55064         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55065     }
55066 });
55067 /*
55068  * Based on:
55069  * Ext JS Library 1.1.1
55070  * Copyright(c) 2006-2007, Ext JS, LLC.
55071  *
55072  * Originally Released Under LGPL - original licence link has changed is not relivant.
55073  *
55074  * Fork - LGPL
55075  * <script type="text/javascript">
55076  */
55077  
55078 // private
55079 // This is a support class used internally by the Grid components
55080 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55081     this.grid = grid;
55082     this.view = grid.getView();
55083     this.proxy = this.view.resizeProxy;
55084     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55085         "gridSplitters" + this.grid.getGridEl().id, {
55086         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55087     });
55088     this.setHandleElId(Roo.id(hd));
55089     this.setOuterHandleElId(Roo.id(hd2));
55090     this.scroll = false;
55091 };
55092 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55093     fly: Roo.Element.fly,
55094
55095     b4StartDrag : function(x, y){
55096         this.view.headersDisabled = true;
55097         this.proxy.setHeight(this.view.mainWrap.getHeight());
55098         var w = this.cm.getColumnWidth(this.cellIndex);
55099         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55100         this.resetConstraints();
55101         this.setXConstraint(minw, 1000);
55102         this.setYConstraint(0, 0);
55103         this.minX = x - minw;
55104         this.maxX = x + 1000;
55105         this.startPos = x;
55106         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55107     },
55108
55109
55110     handleMouseDown : function(e){
55111         ev = Roo.EventObject.setEvent(e);
55112         var t = this.fly(ev.getTarget());
55113         if(t.hasClass("x-grid-split")){
55114             this.cellIndex = this.view.getCellIndex(t.dom);
55115             this.split = t.dom;
55116             this.cm = this.grid.colModel;
55117             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55118                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55119             }
55120         }
55121     },
55122
55123     endDrag : function(e){
55124         this.view.headersDisabled = false;
55125         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55126         var diff = endX - this.startPos;
55127         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55128     },
55129
55130     autoOffset : function(){
55131         this.setDelta(0,0);
55132     }
55133 });/*
55134  * Based on:
55135  * Ext JS Library 1.1.1
55136  * Copyright(c) 2006-2007, Ext JS, LLC.
55137  *
55138  * Originally Released Under LGPL - original licence link has changed is not relivant.
55139  *
55140  * Fork - LGPL
55141  * <script type="text/javascript">
55142  */
55143  
55144 // private
55145 // This is a support class used internally by the Grid components
55146 Roo.grid.GridDragZone = function(grid, config){
55147     this.view = grid.getView();
55148     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55149     if(this.view.lockedBody){
55150         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55151         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55152     }
55153     this.scroll = false;
55154     this.grid = grid;
55155     this.ddel = document.createElement('div');
55156     this.ddel.className = 'x-grid-dd-wrap';
55157 };
55158
55159 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55160     ddGroup : "GridDD",
55161
55162     getDragData : function(e){
55163         var t = Roo.lib.Event.getTarget(e);
55164         var rowIndex = this.view.findRowIndex(t);
55165         var sm = this.grid.selModel;
55166             
55167         //Roo.log(rowIndex);
55168         
55169         if (sm.getSelectedCell) {
55170             // cell selection..
55171             if (!sm.getSelectedCell()) {
55172                 return false;
55173             }
55174             if (rowIndex != sm.getSelectedCell()[0]) {
55175                 return false;
55176             }
55177         
55178         }
55179         
55180         if(rowIndex !== false){
55181             
55182             // if editorgrid.. 
55183             
55184             
55185             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55186                
55187             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55188               //  
55189             //}
55190             if (e.hasModifier()){
55191                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55192             }
55193             
55194             Roo.log("getDragData");
55195             
55196             return {
55197                 grid: this.grid,
55198                 ddel: this.ddel,
55199                 rowIndex: rowIndex,
55200                 selections:sm.getSelections ? sm.getSelections() : (
55201                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55202                 )
55203             };
55204         }
55205         return false;
55206     },
55207
55208     onInitDrag : function(e){
55209         var data = this.dragData;
55210         this.ddel.innerHTML = this.grid.getDragDropText();
55211         this.proxy.update(this.ddel);
55212         // fire start drag?
55213     },
55214
55215     afterRepair : function(){
55216         this.dragging = false;
55217     },
55218
55219     getRepairXY : function(e, data){
55220         return false;
55221     },
55222
55223     onEndDrag : function(data, e){
55224         // fire end drag?
55225     },
55226
55227     onValidDrop : function(dd, e, id){
55228         // fire drag drop?
55229         this.hideProxy();
55230     },
55231
55232     beforeInvalidDrop : function(e, id){
55233
55234     }
55235 });/*
55236  * Based on:
55237  * Ext JS Library 1.1.1
55238  * Copyright(c) 2006-2007, Ext JS, LLC.
55239  *
55240  * Originally Released Under LGPL - original licence link has changed is not relivant.
55241  *
55242  * Fork - LGPL
55243  * <script type="text/javascript">
55244  */
55245  
55246
55247 /**
55248  * @class Roo.grid.ColumnModel
55249  * @extends Roo.util.Observable
55250  * This is the default implementation of a ColumnModel used by the Grid. It defines
55251  * the columns in the grid.
55252  * <br>Usage:<br>
55253  <pre><code>
55254  var colModel = new Roo.grid.ColumnModel([
55255         {header: "Ticker", width: 60, sortable: true, locked: true},
55256         {header: "Company Name", width: 150, sortable: true},
55257         {header: "Market Cap.", width: 100, sortable: true},
55258         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55259         {header: "Employees", width: 100, sortable: true, resizable: false}
55260  ]);
55261  </code></pre>
55262  * <p>
55263  
55264  * The config options listed for this class are options which may appear in each
55265  * individual column definition.
55266  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55267  * @constructor
55268  * @param {Object} config An Array of column config objects. See this class's
55269  * config objects for details.
55270 */
55271 Roo.grid.ColumnModel = function(config){
55272         /**
55273      * The config passed into the constructor
55274      */
55275     this.config = config;
55276     this.lookup = {};
55277
55278     // if no id, create one
55279     // if the column does not have a dataIndex mapping,
55280     // map it to the order it is in the config
55281     for(var i = 0, len = config.length; i < len; i++){
55282         var c = config[i];
55283         if(typeof c.dataIndex == "undefined"){
55284             c.dataIndex = i;
55285         }
55286         if(typeof c.renderer == "string"){
55287             c.renderer = Roo.util.Format[c.renderer];
55288         }
55289         if(typeof c.id == "undefined"){
55290             c.id = Roo.id();
55291         }
55292         if(c.editor && c.editor.xtype){
55293             c.editor  = Roo.factory(c.editor, Roo.grid);
55294         }
55295         if(c.editor && c.editor.isFormField){
55296             c.editor = new Roo.grid.GridEditor(c.editor);
55297         }
55298         this.lookup[c.id] = c;
55299     }
55300
55301     /**
55302      * The width of columns which have no width specified (defaults to 100)
55303      * @type Number
55304      */
55305     this.defaultWidth = 100;
55306
55307     /**
55308      * Default sortable of columns which have no sortable specified (defaults to false)
55309      * @type Boolean
55310      */
55311     this.defaultSortable = false;
55312
55313     this.addEvents({
55314         /**
55315              * @event widthchange
55316              * Fires when the width of a column changes.
55317              * @param {ColumnModel} this
55318              * @param {Number} columnIndex The column index
55319              * @param {Number} newWidth The new width
55320              */
55321             "widthchange": true,
55322         /**
55323              * @event headerchange
55324              * Fires when the text of a header changes.
55325              * @param {ColumnModel} this
55326              * @param {Number} columnIndex The column index
55327              * @param {Number} newText The new header text
55328              */
55329             "headerchange": true,
55330         /**
55331              * @event hiddenchange
55332              * Fires when a column is hidden or "unhidden".
55333              * @param {ColumnModel} this
55334              * @param {Number} columnIndex The column index
55335              * @param {Boolean} hidden true if hidden, false otherwise
55336              */
55337             "hiddenchange": true,
55338             /**
55339          * @event columnmoved
55340          * Fires when a column is moved.
55341          * @param {ColumnModel} this
55342          * @param {Number} oldIndex
55343          * @param {Number} newIndex
55344          */
55345         "columnmoved" : true,
55346         /**
55347          * @event columlockchange
55348          * Fires when a column's locked state is changed
55349          * @param {ColumnModel} this
55350          * @param {Number} colIndex
55351          * @param {Boolean} locked true if locked
55352          */
55353         "columnlockchange" : true
55354     });
55355     Roo.grid.ColumnModel.superclass.constructor.call(this);
55356 };
55357 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55358     /**
55359      * @cfg {String} header The header text to display in the Grid view.
55360      */
55361     /**
55362      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55363      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55364      * specified, the column's index is used as an index into the Record's data Array.
55365      */
55366     /**
55367      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55368      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55369      */
55370     /**
55371      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55372      * Defaults to the value of the {@link #defaultSortable} property.
55373      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55374      */
55375     /**
55376      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55377      */
55378     /**
55379      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55380      */
55381     /**
55382      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55383      */
55384     /**
55385      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55386      */
55387     /**
55388      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55389      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55390      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55391      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55392      */
55393        /**
55394      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55395      */
55396     /**
55397      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55398      */
55399     /**
55400      * @cfg {String} cursor (Optional)
55401      */
55402     /**
55403      * @cfg {String} tooltip (Optional)
55404      */
55405     /**
55406      * Returns the id of the column at the specified index.
55407      * @param {Number} index The column index
55408      * @return {String} the id
55409      */
55410     getColumnId : function(index){
55411         return this.config[index].id;
55412     },
55413
55414     /**
55415      * Returns the column for a specified id.
55416      * @param {String} id The column id
55417      * @return {Object} the column
55418      */
55419     getColumnById : function(id){
55420         return this.lookup[id];
55421     },
55422
55423     
55424     /**
55425      * Returns the column for a specified dataIndex.
55426      * @param {String} dataIndex The column dataIndex
55427      * @return {Object|Boolean} the column or false if not found
55428      */
55429     getColumnByDataIndex: function(dataIndex){
55430         var index = this.findColumnIndex(dataIndex);
55431         return index > -1 ? this.config[index] : false;
55432     },
55433     
55434     /**
55435      * Returns the index for a specified column id.
55436      * @param {String} id The column id
55437      * @return {Number} the index, or -1 if not found
55438      */
55439     getIndexById : function(id){
55440         for(var i = 0, len = this.config.length; i < len; i++){
55441             if(this.config[i].id == id){
55442                 return i;
55443             }
55444         }
55445         return -1;
55446     },
55447     
55448     /**
55449      * Returns the index for a specified column dataIndex.
55450      * @param {String} dataIndex The column dataIndex
55451      * @return {Number} the index, or -1 if not found
55452      */
55453     
55454     findColumnIndex : function(dataIndex){
55455         for(var i = 0, len = this.config.length; i < len; i++){
55456             if(this.config[i].dataIndex == dataIndex){
55457                 return i;
55458             }
55459         }
55460         return -1;
55461     },
55462     
55463     
55464     moveColumn : function(oldIndex, newIndex){
55465         var c = this.config[oldIndex];
55466         this.config.splice(oldIndex, 1);
55467         this.config.splice(newIndex, 0, c);
55468         this.dataMap = null;
55469         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55470     },
55471
55472     isLocked : function(colIndex){
55473         return this.config[colIndex].locked === true;
55474     },
55475
55476     setLocked : function(colIndex, value, suppressEvent){
55477         if(this.isLocked(colIndex) == value){
55478             return;
55479         }
55480         this.config[colIndex].locked = value;
55481         if(!suppressEvent){
55482             this.fireEvent("columnlockchange", this, colIndex, value);
55483         }
55484     },
55485
55486     getTotalLockedWidth : function(){
55487         var totalWidth = 0;
55488         for(var i = 0; i < this.config.length; i++){
55489             if(this.isLocked(i) && !this.isHidden(i)){
55490                 this.totalWidth += this.getColumnWidth(i);
55491             }
55492         }
55493         return totalWidth;
55494     },
55495
55496     getLockedCount : function(){
55497         for(var i = 0, len = this.config.length; i < len; i++){
55498             if(!this.isLocked(i)){
55499                 return i;
55500             }
55501         }
55502     },
55503
55504     /**
55505      * Returns the number of columns.
55506      * @return {Number}
55507      */
55508     getColumnCount : function(visibleOnly){
55509         if(visibleOnly === true){
55510             var c = 0;
55511             for(var i = 0, len = this.config.length; i < len; i++){
55512                 if(!this.isHidden(i)){
55513                     c++;
55514                 }
55515             }
55516             return c;
55517         }
55518         return this.config.length;
55519     },
55520
55521     /**
55522      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55523      * @param {Function} fn
55524      * @param {Object} scope (optional)
55525      * @return {Array} result
55526      */
55527     getColumnsBy : function(fn, scope){
55528         var r = [];
55529         for(var i = 0, len = this.config.length; i < len; i++){
55530             var c = this.config[i];
55531             if(fn.call(scope||this, c, i) === true){
55532                 r[r.length] = c;
55533             }
55534         }
55535         return r;
55536     },
55537
55538     /**
55539      * Returns true if the specified column is sortable.
55540      * @param {Number} col The column index
55541      * @return {Boolean}
55542      */
55543     isSortable : function(col){
55544         if(typeof this.config[col].sortable == "undefined"){
55545             return this.defaultSortable;
55546         }
55547         return this.config[col].sortable;
55548     },
55549
55550     /**
55551      * Returns the rendering (formatting) function defined for the column.
55552      * @param {Number} col The column index.
55553      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55554      */
55555     getRenderer : function(col){
55556         if(!this.config[col].renderer){
55557             return Roo.grid.ColumnModel.defaultRenderer;
55558         }
55559         return this.config[col].renderer;
55560     },
55561
55562     /**
55563      * Sets the rendering (formatting) function for a column.
55564      * @param {Number} col The column index
55565      * @param {Function} fn The function to use to process the cell's raw data
55566      * to return HTML markup for the grid view. The render function is called with
55567      * the following parameters:<ul>
55568      * <li>Data value.</li>
55569      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55570      * <li>css A CSS style string to apply to the table cell.</li>
55571      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55572      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55573      * <li>Row index</li>
55574      * <li>Column index</li>
55575      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55576      */
55577     setRenderer : function(col, fn){
55578         this.config[col].renderer = fn;
55579     },
55580
55581     /**
55582      * Returns the width for the specified column.
55583      * @param {Number} col The column index
55584      * @return {Number}
55585      */
55586     getColumnWidth : function(col){
55587         return this.config[col].width * 1 || this.defaultWidth;
55588     },
55589
55590     /**
55591      * Sets the width for a column.
55592      * @param {Number} col The column index
55593      * @param {Number} width The new width
55594      */
55595     setColumnWidth : function(col, width, suppressEvent){
55596         this.config[col].width = width;
55597         this.totalWidth = null;
55598         if(!suppressEvent){
55599              this.fireEvent("widthchange", this, col, width);
55600         }
55601     },
55602
55603     /**
55604      * Returns the total width of all columns.
55605      * @param {Boolean} includeHidden True to include hidden column widths
55606      * @return {Number}
55607      */
55608     getTotalWidth : function(includeHidden){
55609         if(!this.totalWidth){
55610             this.totalWidth = 0;
55611             for(var i = 0, len = this.config.length; i < len; i++){
55612                 if(includeHidden || !this.isHidden(i)){
55613                     this.totalWidth += this.getColumnWidth(i);
55614                 }
55615             }
55616         }
55617         return this.totalWidth;
55618     },
55619
55620     /**
55621      * Returns the header for the specified column.
55622      * @param {Number} col The column index
55623      * @return {String}
55624      */
55625     getColumnHeader : function(col){
55626         return this.config[col].header;
55627     },
55628
55629     /**
55630      * Sets the header for a column.
55631      * @param {Number} col The column index
55632      * @param {String} header The new header
55633      */
55634     setColumnHeader : function(col, header){
55635         this.config[col].header = header;
55636         this.fireEvent("headerchange", this, col, header);
55637     },
55638
55639     /**
55640      * Returns the tooltip for the specified column.
55641      * @param {Number} col The column index
55642      * @return {String}
55643      */
55644     getColumnTooltip : function(col){
55645             return this.config[col].tooltip;
55646     },
55647     /**
55648      * Sets the tooltip for a column.
55649      * @param {Number} col The column index
55650      * @param {String} tooltip The new tooltip
55651      */
55652     setColumnTooltip : function(col, tooltip){
55653             this.config[col].tooltip = tooltip;
55654     },
55655
55656     /**
55657      * Returns the dataIndex for the specified column.
55658      * @param {Number} col The column index
55659      * @return {Number}
55660      */
55661     getDataIndex : function(col){
55662         return this.config[col].dataIndex;
55663     },
55664
55665     /**
55666      * Sets the dataIndex for a column.
55667      * @param {Number} col The column index
55668      * @param {Number} dataIndex The new dataIndex
55669      */
55670     setDataIndex : function(col, dataIndex){
55671         this.config[col].dataIndex = dataIndex;
55672     },
55673
55674     
55675     
55676     /**
55677      * Returns true if the cell is editable.
55678      * @param {Number} colIndex The column index
55679      * @param {Number} rowIndex The row index
55680      * @return {Boolean}
55681      */
55682     isCellEditable : function(colIndex, rowIndex){
55683         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55684     },
55685
55686     /**
55687      * Returns the editor defined for the cell/column.
55688      * return false or null to disable editing.
55689      * @param {Number} colIndex The column index
55690      * @param {Number} rowIndex The row index
55691      * @return {Object}
55692      */
55693     getCellEditor : function(colIndex, rowIndex){
55694         return this.config[colIndex].editor;
55695     },
55696
55697     /**
55698      * Sets if a column is editable.
55699      * @param {Number} col The column index
55700      * @param {Boolean} editable True if the column is editable
55701      */
55702     setEditable : function(col, editable){
55703         this.config[col].editable = editable;
55704     },
55705
55706
55707     /**
55708      * Returns true if the column is hidden.
55709      * @param {Number} colIndex The column index
55710      * @return {Boolean}
55711      */
55712     isHidden : function(colIndex){
55713         return this.config[colIndex].hidden;
55714     },
55715
55716
55717     /**
55718      * Returns true if the column width cannot be changed
55719      */
55720     isFixed : function(colIndex){
55721         return this.config[colIndex].fixed;
55722     },
55723
55724     /**
55725      * Returns true if the column can be resized
55726      * @return {Boolean}
55727      */
55728     isResizable : function(colIndex){
55729         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55730     },
55731     /**
55732      * Sets if a column is hidden.
55733      * @param {Number} colIndex The column index
55734      * @param {Boolean} hidden True if the column is hidden
55735      */
55736     setHidden : function(colIndex, hidden){
55737         this.config[colIndex].hidden = hidden;
55738         this.totalWidth = null;
55739         this.fireEvent("hiddenchange", this, colIndex, hidden);
55740     },
55741
55742     /**
55743      * Sets the editor for a column.
55744      * @param {Number} col The column index
55745      * @param {Object} editor The editor object
55746      */
55747     setEditor : function(col, editor){
55748         this.config[col].editor = editor;
55749     }
55750 });
55751
55752 Roo.grid.ColumnModel.defaultRenderer = function(value){
55753         if(typeof value == "string" && value.length < 1){
55754             return "&#160;";
55755         }
55756         return value;
55757 };
55758
55759 // Alias for backwards compatibility
55760 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55761 /*
55762  * Based on:
55763  * Ext JS Library 1.1.1
55764  * Copyright(c) 2006-2007, Ext JS, LLC.
55765  *
55766  * Originally Released Under LGPL - original licence link has changed is not relivant.
55767  *
55768  * Fork - LGPL
55769  * <script type="text/javascript">
55770  */
55771
55772 /**
55773  * @class Roo.grid.AbstractSelectionModel
55774  * @extends Roo.util.Observable
55775  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55776  * implemented by descendant classes.  This class should not be directly instantiated.
55777  * @constructor
55778  */
55779 Roo.grid.AbstractSelectionModel = function(){
55780     this.locked = false;
55781     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55782 };
55783
55784 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55785     /** @ignore Called by the grid automatically. Do not call directly. */
55786     init : function(grid){
55787         this.grid = grid;
55788         this.initEvents();
55789     },
55790
55791     /**
55792      * Locks the selections.
55793      */
55794     lock : function(){
55795         this.locked = true;
55796     },
55797
55798     /**
55799      * Unlocks the selections.
55800      */
55801     unlock : function(){
55802         this.locked = false;
55803     },
55804
55805     /**
55806      * Returns true if the selections are locked.
55807      * @return {Boolean}
55808      */
55809     isLocked : function(){
55810         return this.locked;
55811     }
55812 });/*
55813  * Based on:
55814  * Ext JS Library 1.1.1
55815  * Copyright(c) 2006-2007, Ext JS, LLC.
55816  *
55817  * Originally Released Under LGPL - original licence link has changed is not relivant.
55818  *
55819  * Fork - LGPL
55820  * <script type="text/javascript">
55821  */
55822 /**
55823  * @extends Roo.grid.AbstractSelectionModel
55824  * @class Roo.grid.RowSelectionModel
55825  * The default SelectionModel used by {@link Roo.grid.Grid}.
55826  * It supports multiple selections and keyboard selection/navigation. 
55827  * @constructor
55828  * @param {Object} config
55829  */
55830 Roo.grid.RowSelectionModel = function(config){
55831     Roo.apply(this, config);
55832     this.selections = new Roo.util.MixedCollection(false, function(o){
55833         return o.id;
55834     });
55835
55836     this.last = false;
55837     this.lastActive = false;
55838
55839     this.addEvents({
55840         /**
55841              * @event selectionchange
55842              * Fires when the selection changes
55843              * @param {SelectionModel} this
55844              */
55845             "selectionchange" : true,
55846         /**
55847              * @event afterselectionchange
55848              * Fires after the selection changes (eg. by key press or clicking)
55849              * @param {SelectionModel} this
55850              */
55851             "afterselectionchange" : true,
55852         /**
55853              * @event beforerowselect
55854              * Fires when a row is selected being selected, return false to cancel.
55855              * @param {SelectionModel} this
55856              * @param {Number} rowIndex The selected index
55857              * @param {Boolean} keepExisting False if other selections will be cleared
55858              */
55859             "beforerowselect" : true,
55860         /**
55861              * @event rowselect
55862              * Fires when a row is selected.
55863              * @param {SelectionModel} this
55864              * @param {Number} rowIndex The selected index
55865              * @param {Roo.data.Record} r The record
55866              */
55867             "rowselect" : true,
55868         /**
55869              * @event rowdeselect
55870              * Fires when a row is deselected.
55871              * @param {SelectionModel} this
55872              * @param {Number} rowIndex The selected index
55873              */
55874         "rowdeselect" : true
55875     });
55876     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55877     this.locked = false;
55878 };
55879
55880 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55881     /**
55882      * @cfg {Boolean} singleSelect
55883      * True to allow selection of only one row at a time (defaults to false)
55884      */
55885     singleSelect : false,
55886
55887     // private
55888     initEvents : function(){
55889
55890         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55891             this.grid.on("mousedown", this.handleMouseDown, this);
55892         }else{ // allow click to work like normal
55893             this.grid.on("rowclick", this.handleDragableRowClick, this);
55894         }
55895
55896         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55897             "up" : function(e){
55898                 if(!e.shiftKey){
55899                     this.selectPrevious(e.shiftKey);
55900                 }else if(this.last !== false && this.lastActive !== false){
55901                     var last = this.last;
55902                     this.selectRange(this.last,  this.lastActive-1);
55903                     this.grid.getView().focusRow(this.lastActive);
55904                     if(last !== false){
55905                         this.last = last;
55906                     }
55907                 }else{
55908                     this.selectFirstRow();
55909                 }
55910                 this.fireEvent("afterselectionchange", this);
55911             },
55912             "down" : function(e){
55913                 if(!e.shiftKey){
55914                     this.selectNext(e.shiftKey);
55915                 }else if(this.last !== false && this.lastActive !== false){
55916                     var last = this.last;
55917                     this.selectRange(this.last,  this.lastActive+1);
55918                     this.grid.getView().focusRow(this.lastActive);
55919                     if(last !== false){
55920                         this.last = last;
55921                     }
55922                 }else{
55923                     this.selectFirstRow();
55924                 }
55925                 this.fireEvent("afterselectionchange", this);
55926             },
55927             scope: this
55928         });
55929
55930         var view = this.grid.view;
55931         view.on("refresh", this.onRefresh, this);
55932         view.on("rowupdated", this.onRowUpdated, this);
55933         view.on("rowremoved", this.onRemove, this);
55934     },
55935
55936     // private
55937     onRefresh : function(){
55938         var ds = this.grid.dataSource, i, v = this.grid.view;
55939         var s = this.selections;
55940         s.each(function(r){
55941             if((i = ds.indexOfId(r.id)) != -1){
55942                 v.onRowSelect(i);
55943                 s.add(ds.getAt(i)); // updating the selection relate data
55944             }else{
55945                 s.remove(r);
55946             }
55947         });
55948     },
55949
55950     // private
55951     onRemove : function(v, index, r){
55952         this.selections.remove(r);
55953     },
55954
55955     // private
55956     onRowUpdated : function(v, index, r){
55957         if(this.isSelected(r)){
55958             v.onRowSelect(index);
55959         }
55960     },
55961
55962     /**
55963      * Select records.
55964      * @param {Array} records The records to select
55965      * @param {Boolean} keepExisting (optional) True to keep existing selections
55966      */
55967     selectRecords : function(records, keepExisting){
55968         if(!keepExisting){
55969             this.clearSelections();
55970         }
55971         var ds = this.grid.dataSource;
55972         for(var i = 0, len = records.length; i < len; i++){
55973             this.selectRow(ds.indexOf(records[i]), true);
55974         }
55975     },
55976
55977     /**
55978      * Gets the number of selected rows.
55979      * @return {Number}
55980      */
55981     getCount : function(){
55982         return this.selections.length;
55983     },
55984
55985     /**
55986      * Selects the first row in the grid.
55987      */
55988     selectFirstRow : function(){
55989         this.selectRow(0);
55990     },
55991
55992     /**
55993      * Select the last row.
55994      * @param {Boolean} keepExisting (optional) True to keep existing selections
55995      */
55996     selectLastRow : function(keepExisting){
55997         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55998     },
55999
56000     /**
56001      * Selects the row immediately following the last selected row.
56002      * @param {Boolean} keepExisting (optional) True to keep existing selections
56003      */
56004     selectNext : function(keepExisting){
56005         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
56006             this.selectRow(this.last+1, keepExisting);
56007             this.grid.getView().focusRow(this.last);
56008         }
56009     },
56010
56011     /**
56012      * Selects the row that precedes the last selected row.
56013      * @param {Boolean} keepExisting (optional) True to keep existing selections
56014      */
56015     selectPrevious : function(keepExisting){
56016         if(this.last){
56017             this.selectRow(this.last-1, keepExisting);
56018             this.grid.getView().focusRow(this.last);
56019         }
56020     },
56021
56022     /**
56023      * Returns the selected records
56024      * @return {Array} Array of selected records
56025      */
56026     getSelections : function(){
56027         return [].concat(this.selections.items);
56028     },
56029
56030     /**
56031      * Returns the first selected record.
56032      * @return {Record}
56033      */
56034     getSelected : function(){
56035         return this.selections.itemAt(0);
56036     },
56037
56038
56039     /**
56040      * Clears all selections.
56041      */
56042     clearSelections : function(fast){
56043         if(this.locked) return;
56044         if(fast !== true){
56045             var ds = this.grid.dataSource;
56046             var s = this.selections;
56047             s.each(function(r){
56048                 this.deselectRow(ds.indexOfId(r.id));
56049             }, this);
56050             s.clear();
56051         }else{
56052             this.selections.clear();
56053         }
56054         this.last = false;
56055     },
56056
56057
56058     /**
56059      * Selects all rows.
56060      */
56061     selectAll : function(){
56062         if(this.locked) return;
56063         this.selections.clear();
56064         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56065             this.selectRow(i, true);
56066         }
56067     },
56068
56069     /**
56070      * Returns True if there is a selection.
56071      * @return {Boolean}
56072      */
56073     hasSelection : function(){
56074         return this.selections.length > 0;
56075     },
56076
56077     /**
56078      * Returns True if the specified row is selected.
56079      * @param {Number/Record} record The record or index of the record to check
56080      * @return {Boolean}
56081      */
56082     isSelected : function(index){
56083         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56084         return (r && this.selections.key(r.id) ? true : false);
56085     },
56086
56087     /**
56088      * Returns True if the specified record id is selected.
56089      * @param {String} id The id of record to check
56090      * @return {Boolean}
56091      */
56092     isIdSelected : function(id){
56093         return (this.selections.key(id) ? true : false);
56094     },
56095
56096     // private
56097     handleMouseDown : function(e, t){
56098         var view = this.grid.getView(), rowIndex;
56099         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56100             return;
56101         };
56102         if(e.shiftKey && this.last !== false){
56103             var last = this.last;
56104             this.selectRange(last, rowIndex, e.ctrlKey);
56105             this.last = last; // reset the last
56106             view.focusRow(rowIndex);
56107         }else{
56108             var isSelected = this.isSelected(rowIndex);
56109             if(e.button !== 0 && isSelected){
56110                 view.focusRow(rowIndex);
56111             }else if(e.ctrlKey && isSelected){
56112                 this.deselectRow(rowIndex);
56113             }else if(!isSelected){
56114                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56115                 view.focusRow(rowIndex);
56116             }
56117         }
56118         this.fireEvent("afterselectionchange", this);
56119     },
56120     // private
56121     handleDragableRowClick :  function(grid, rowIndex, e) 
56122     {
56123         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56124             this.selectRow(rowIndex, false);
56125             grid.view.focusRow(rowIndex);
56126              this.fireEvent("afterselectionchange", this);
56127         }
56128     },
56129     
56130     /**
56131      * Selects multiple rows.
56132      * @param {Array} rows Array of the indexes of the row to select
56133      * @param {Boolean} keepExisting (optional) True to keep existing selections
56134      */
56135     selectRows : function(rows, keepExisting){
56136         if(!keepExisting){
56137             this.clearSelections();
56138         }
56139         for(var i = 0, len = rows.length; i < len; i++){
56140             this.selectRow(rows[i], true);
56141         }
56142     },
56143
56144     /**
56145      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56146      * @param {Number} startRow The index of the first row in the range
56147      * @param {Number} endRow The index of the last row in the range
56148      * @param {Boolean} keepExisting (optional) True to retain existing selections
56149      */
56150     selectRange : function(startRow, endRow, keepExisting){
56151         if(this.locked) return;
56152         if(!keepExisting){
56153             this.clearSelections();
56154         }
56155         if(startRow <= endRow){
56156             for(var i = startRow; i <= endRow; i++){
56157                 this.selectRow(i, true);
56158             }
56159         }else{
56160             for(var i = startRow; i >= endRow; i--){
56161                 this.selectRow(i, true);
56162             }
56163         }
56164     },
56165
56166     /**
56167      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56168      * @param {Number} startRow The index of the first row in the range
56169      * @param {Number} endRow The index of the last row in the range
56170      */
56171     deselectRange : function(startRow, endRow, preventViewNotify){
56172         if(this.locked) return;
56173         for(var i = startRow; i <= endRow; i++){
56174             this.deselectRow(i, preventViewNotify);
56175         }
56176     },
56177
56178     /**
56179      * Selects a row.
56180      * @param {Number} row The index of the row to select
56181      * @param {Boolean} keepExisting (optional) True to keep existing selections
56182      */
56183     selectRow : function(index, keepExisting, preventViewNotify){
56184         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56185         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56186             if(!keepExisting || this.singleSelect){
56187                 this.clearSelections();
56188             }
56189             var r = this.grid.dataSource.getAt(index);
56190             this.selections.add(r);
56191             this.last = this.lastActive = index;
56192             if(!preventViewNotify){
56193                 this.grid.getView().onRowSelect(index);
56194             }
56195             this.fireEvent("rowselect", this, index, r);
56196             this.fireEvent("selectionchange", this);
56197         }
56198     },
56199
56200     /**
56201      * Deselects a row.
56202      * @param {Number} row The index of the row to deselect
56203      */
56204     deselectRow : function(index, preventViewNotify){
56205         if(this.locked) return;
56206         if(this.last == index){
56207             this.last = false;
56208         }
56209         if(this.lastActive == index){
56210             this.lastActive = false;
56211         }
56212         var r = this.grid.dataSource.getAt(index);
56213         this.selections.remove(r);
56214         if(!preventViewNotify){
56215             this.grid.getView().onRowDeselect(index);
56216         }
56217         this.fireEvent("rowdeselect", this, index);
56218         this.fireEvent("selectionchange", this);
56219     },
56220
56221     // private
56222     restoreLast : function(){
56223         if(this._last){
56224             this.last = this._last;
56225         }
56226     },
56227
56228     // private
56229     acceptsNav : function(row, col, cm){
56230         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56231     },
56232
56233     // private
56234     onEditorKey : function(field, e){
56235         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56236         if(k == e.TAB){
56237             e.stopEvent();
56238             ed.completeEdit();
56239             if(e.shiftKey){
56240                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56241             }else{
56242                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56243             }
56244         }else if(k == e.ENTER && !e.ctrlKey){
56245             e.stopEvent();
56246             ed.completeEdit();
56247             if(e.shiftKey){
56248                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56249             }else{
56250                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56251             }
56252         }else if(k == e.ESC){
56253             ed.cancelEdit();
56254         }
56255         if(newCell){
56256             g.startEditing(newCell[0], newCell[1]);
56257         }
56258     }
56259 });/*
56260  * Based on:
56261  * Ext JS Library 1.1.1
56262  * Copyright(c) 2006-2007, Ext JS, LLC.
56263  *
56264  * Originally Released Under LGPL - original licence link has changed is not relivant.
56265  *
56266  * Fork - LGPL
56267  * <script type="text/javascript">
56268  */
56269 /**
56270  * @class Roo.grid.CellSelectionModel
56271  * @extends Roo.grid.AbstractSelectionModel
56272  * This class provides the basic implementation for cell selection in a grid.
56273  * @constructor
56274  * @param {Object} config The object containing the configuration of this model.
56275  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56276  */
56277 Roo.grid.CellSelectionModel = function(config){
56278     Roo.apply(this, config);
56279
56280     this.selection = null;
56281
56282     this.addEvents({
56283         /**
56284              * @event beforerowselect
56285              * Fires before a cell is selected.
56286              * @param {SelectionModel} this
56287              * @param {Number} rowIndex The selected row index
56288              * @param {Number} colIndex The selected cell index
56289              */
56290             "beforecellselect" : true,
56291         /**
56292              * @event cellselect
56293              * Fires when a cell is selected.
56294              * @param {SelectionModel} this
56295              * @param {Number} rowIndex The selected row index
56296              * @param {Number} colIndex The selected cell index
56297              */
56298             "cellselect" : true,
56299         /**
56300              * @event selectionchange
56301              * Fires when the active selection changes.
56302              * @param {SelectionModel} this
56303              * @param {Object} selection null for no selection or an object (o) with two properties
56304                 <ul>
56305                 <li>o.record: the record object for the row the selection is in</li>
56306                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56307                 </ul>
56308              */
56309             "selectionchange" : true,
56310         /**
56311              * @event tabend
56312              * Fires when the tab (or enter) was pressed on the last editable cell
56313              * You can use this to trigger add new row.
56314              * @param {SelectionModel} this
56315              */
56316             "tabend" : true,
56317          /**
56318              * @event beforeeditnext
56319              * Fires before the next editable sell is made active
56320              * You can use this to skip to another cell or fire the tabend
56321              *    if you set cell to false
56322              * @param {Object} eventdata object : { cell : [ row, col ] } 
56323              */
56324             "beforeeditnext" : true
56325     });
56326     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56327 };
56328
56329 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56330     
56331     enter_is_tab: false,
56332
56333     /** @ignore */
56334     initEvents : function(){
56335         this.grid.on("mousedown", this.handleMouseDown, this);
56336         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56337         var view = this.grid.view;
56338         view.on("refresh", this.onViewChange, this);
56339         view.on("rowupdated", this.onRowUpdated, this);
56340         view.on("beforerowremoved", this.clearSelections, this);
56341         view.on("beforerowsinserted", this.clearSelections, this);
56342         if(this.grid.isEditor){
56343             this.grid.on("beforeedit", this.beforeEdit,  this);
56344         }
56345     },
56346
56347         //private
56348     beforeEdit : function(e){
56349         this.select(e.row, e.column, false, true, e.record);
56350     },
56351
56352         //private
56353     onRowUpdated : function(v, index, r){
56354         if(this.selection && this.selection.record == r){
56355             v.onCellSelect(index, this.selection.cell[1]);
56356         }
56357     },
56358
56359         //private
56360     onViewChange : function(){
56361         this.clearSelections(true);
56362     },
56363
56364         /**
56365          * Returns the currently selected cell,.
56366          * @return {Array} The selected cell (row, column) or null if none selected.
56367          */
56368     getSelectedCell : function(){
56369         return this.selection ? this.selection.cell : null;
56370     },
56371
56372     /**
56373      * Clears all selections.
56374      * @param {Boolean} true to prevent the gridview from being notified about the change.
56375      */
56376     clearSelections : function(preventNotify){
56377         var s = this.selection;
56378         if(s){
56379             if(preventNotify !== true){
56380                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56381             }
56382             this.selection = null;
56383             this.fireEvent("selectionchange", this, null);
56384         }
56385     },
56386
56387     /**
56388      * Returns true if there is a selection.
56389      * @return {Boolean}
56390      */
56391     hasSelection : function(){
56392         return this.selection ? true : false;
56393     },
56394
56395     /** @ignore */
56396     handleMouseDown : function(e, t){
56397         var v = this.grid.getView();
56398         if(this.isLocked()){
56399             return;
56400         };
56401         var row = v.findRowIndex(t);
56402         var cell = v.findCellIndex(t);
56403         if(row !== false && cell !== false){
56404             this.select(row, cell);
56405         }
56406     },
56407
56408     /**
56409      * Selects a cell.
56410      * @param {Number} rowIndex
56411      * @param {Number} collIndex
56412      */
56413     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56414         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56415             this.clearSelections();
56416             r = r || this.grid.dataSource.getAt(rowIndex);
56417             this.selection = {
56418                 record : r,
56419                 cell : [rowIndex, colIndex]
56420             };
56421             if(!preventViewNotify){
56422                 var v = this.grid.getView();
56423                 v.onCellSelect(rowIndex, colIndex);
56424                 if(preventFocus !== true){
56425                     v.focusCell(rowIndex, colIndex);
56426                 }
56427             }
56428             this.fireEvent("cellselect", this, rowIndex, colIndex);
56429             this.fireEvent("selectionchange", this, this.selection);
56430         }
56431     },
56432
56433         //private
56434     isSelectable : function(rowIndex, colIndex, cm){
56435         return !cm.isHidden(colIndex);
56436     },
56437
56438     /** @ignore */
56439     handleKeyDown : function(e){
56440         //Roo.log('Cell Sel Model handleKeyDown');
56441         if(!e.isNavKeyPress()){
56442             return;
56443         }
56444         var g = this.grid, s = this.selection;
56445         if(!s){
56446             e.stopEvent();
56447             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56448             if(cell){
56449                 this.select(cell[0], cell[1]);
56450             }
56451             return;
56452         }
56453         var sm = this;
56454         var walk = function(row, col, step){
56455             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56456         };
56457         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56458         var newCell;
56459
56460       
56461
56462         switch(k){
56463             case e.TAB:
56464                 // handled by onEditorKey
56465                 if (g.isEditor && g.editing) {
56466                     return;
56467                 }
56468                 if(e.shiftKey) {
56469                     newCell = walk(r, c-1, -1);
56470                 } else {
56471                     newCell = walk(r, c+1, 1);
56472                 }
56473                 break;
56474             
56475             case e.DOWN:
56476                newCell = walk(r+1, c, 1);
56477                 break;
56478             
56479             case e.UP:
56480                 newCell = walk(r-1, c, -1);
56481                 break;
56482             
56483             case e.RIGHT:
56484                 newCell = walk(r, c+1, 1);
56485                 break;
56486             
56487             case e.LEFT:
56488                 newCell = walk(r, c-1, -1);
56489                 break;
56490             
56491             case e.ENTER:
56492                 
56493                 if(g.isEditor && !g.editing){
56494                    g.startEditing(r, c);
56495                    e.stopEvent();
56496                    return;
56497                 }
56498                 
56499                 
56500              break;
56501         };
56502         if(newCell){
56503             this.select(newCell[0], newCell[1]);
56504             e.stopEvent();
56505             
56506         }
56507     },
56508
56509     acceptsNav : function(row, col, cm){
56510         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56511     },
56512     /**
56513      * Selects a cell.
56514      * @param {Number} field (not used) - as it's normally used as a listener
56515      * @param {Number} e - event - fake it by using
56516      *
56517      * var e = Roo.EventObjectImpl.prototype;
56518      * e.keyCode = e.TAB
56519      *
56520      * 
56521      */
56522     onEditorKey : function(field, e){
56523         
56524         var k = e.getKey(),
56525             newCell,
56526             g = this.grid,
56527             ed = g.activeEditor,
56528             forward = false;
56529         ///Roo.log('onEditorKey' + k);
56530         
56531         
56532         if (this.enter_is_tab && k == e.ENTER) {
56533             k = e.TAB;
56534         }
56535         
56536         if(k == e.TAB){
56537             if(e.shiftKey){
56538                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56539             }else{
56540                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56541                 forward = true;
56542             }
56543             
56544             e.stopEvent();
56545             
56546         } else if(k == e.ENTER &&  !e.ctrlKey){
56547             ed.completeEdit();
56548             e.stopEvent();
56549             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56550         
56551                 } else if(k == e.ESC){
56552             ed.cancelEdit();
56553         }
56554                 
56555         if (newCell) {
56556             var ecall = { cell : newCell, forward : forward };
56557             this.fireEvent('beforeeditnext', ecall );
56558             newCell = ecall.cell;
56559                         forward = ecall.forward;
56560         }
56561                 
56562         if(newCell){
56563             //Roo.log('next cell after edit');
56564             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56565         } else if (forward) {
56566             // tabbed past last
56567             this.fireEvent.defer(100, this, ['tabend',this]);
56568         }
56569     }
56570 });/*
56571  * Based on:
56572  * Ext JS Library 1.1.1
56573  * Copyright(c) 2006-2007, Ext JS, LLC.
56574  *
56575  * Originally Released Under LGPL - original licence link has changed is not relivant.
56576  *
56577  * Fork - LGPL
56578  * <script type="text/javascript">
56579  */
56580  
56581 /**
56582  * @class Roo.grid.EditorGrid
56583  * @extends Roo.grid.Grid
56584  * Class for creating and editable grid.
56585  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56586  * The container MUST have some type of size defined for the grid to fill. The container will be 
56587  * automatically set to position relative if it isn't already.
56588  * @param {Object} dataSource The data model to bind to
56589  * @param {Object} colModel The column model with info about this grid's columns
56590  */
56591 Roo.grid.EditorGrid = function(container, config){
56592     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56593     this.getGridEl().addClass("xedit-grid");
56594
56595     if(!this.selModel){
56596         this.selModel = new Roo.grid.CellSelectionModel();
56597     }
56598
56599     this.activeEditor = null;
56600
56601         this.addEvents({
56602             /**
56603              * @event beforeedit
56604              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56605              * <ul style="padding:5px;padding-left:16px;">
56606              * <li>grid - This grid</li>
56607              * <li>record - The record being edited</li>
56608              * <li>field - The field name being edited</li>
56609              * <li>value - The value for the field being edited.</li>
56610              * <li>row - The grid row index</li>
56611              * <li>column - The grid column index</li>
56612              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56613              * </ul>
56614              * @param {Object} e An edit event (see above for description)
56615              */
56616             "beforeedit" : true,
56617             /**
56618              * @event afteredit
56619              * Fires after a cell is edited. <br />
56620              * <ul style="padding:5px;padding-left:16px;">
56621              * <li>grid - This grid</li>
56622              * <li>record - The record being edited</li>
56623              * <li>field - The field name being edited</li>
56624              * <li>value - The value being set</li>
56625              * <li>originalValue - The original value for the field, before the edit.</li>
56626              * <li>row - The grid row index</li>
56627              * <li>column - The grid column index</li>
56628              * </ul>
56629              * @param {Object} e An edit event (see above for description)
56630              */
56631             "afteredit" : true,
56632             /**
56633              * @event validateedit
56634              * Fires after a cell is edited, but before the value is set in the record. 
56635          * You can use this to modify the value being set in the field, Return false
56636              * to cancel the change. The edit event object has the following properties <br />
56637              * <ul style="padding:5px;padding-left:16px;">
56638          * <li>editor - This editor</li>
56639              * <li>grid - This grid</li>
56640              * <li>record - The record being edited</li>
56641              * <li>field - The field name being edited</li>
56642              * <li>value - The value being set</li>
56643              * <li>originalValue - The original value for the field, before the edit.</li>
56644              * <li>row - The grid row index</li>
56645              * <li>column - The grid column index</li>
56646              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56647              * </ul>
56648              * @param {Object} e An edit event (see above for description)
56649              */
56650             "validateedit" : true
56651         });
56652     this.on("bodyscroll", this.stopEditing,  this);
56653     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56654 };
56655
56656 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56657     /**
56658      * @cfg {Number} clicksToEdit
56659      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56660      */
56661     clicksToEdit: 2,
56662
56663     // private
56664     isEditor : true,
56665     // private
56666     trackMouseOver: false, // causes very odd FF errors
56667
56668     onCellDblClick : function(g, row, col){
56669         this.startEditing(row, col);
56670     },
56671
56672     onEditComplete : function(ed, value, startValue){
56673         this.editing = false;
56674         this.activeEditor = null;
56675         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56676         var r = ed.record;
56677         var field = this.colModel.getDataIndex(ed.col);
56678         var e = {
56679             grid: this,
56680             record: r,
56681             field: field,
56682             originalValue: startValue,
56683             value: value,
56684             row: ed.row,
56685             column: ed.col,
56686             cancel:false,
56687             editor: ed
56688         };
56689         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56690         cell.show();
56691           
56692         if(String(value) !== String(startValue)){
56693             
56694             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56695                 r.set(field, e.value);
56696                 // if we are dealing with a combo box..
56697                 // then we also set the 'name' colum to be the displayField
56698                 if (ed.field.displayField && ed.field.name) {
56699                     r.set(ed.field.name, ed.field.el.dom.value);
56700                 }
56701                 
56702                 delete e.cancel; //?? why!!!
56703                 this.fireEvent("afteredit", e);
56704             }
56705         } else {
56706             this.fireEvent("afteredit", e); // always fire it!
56707         }
56708         this.view.focusCell(ed.row, ed.col);
56709     },
56710
56711     /**
56712      * Starts editing the specified for the specified row/column
56713      * @param {Number} rowIndex
56714      * @param {Number} colIndex
56715      */
56716     startEditing : function(row, col){
56717         this.stopEditing();
56718         if(this.colModel.isCellEditable(col, row)){
56719             this.view.ensureVisible(row, col, true);
56720           
56721             var r = this.dataSource.getAt(row);
56722             var field = this.colModel.getDataIndex(col);
56723             var cell = Roo.get(this.view.getCell(row,col));
56724             var e = {
56725                 grid: this,
56726                 record: r,
56727                 field: field,
56728                 value: r.data[field],
56729                 row: row,
56730                 column: col,
56731                 cancel:false 
56732             };
56733             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56734                 this.editing = true;
56735                 var ed = this.colModel.getCellEditor(col, row);
56736                 
56737                 if (!ed) {
56738                     return;
56739                 }
56740                 if(!ed.rendered){
56741                     ed.render(ed.parentEl || document.body);
56742                 }
56743                 ed.field.reset();
56744                
56745                 cell.hide();
56746                 
56747                 (function(){ // complex but required for focus issues in safari, ie and opera
56748                     ed.row = row;
56749                     ed.col = col;
56750                     ed.record = r;
56751                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56752                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56753                     this.activeEditor = ed;
56754                     var v = r.data[field];
56755                     ed.startEdit(this.view.getCell(row, col), v);
56756                     // combo's with 'displayField and name set
56757                     if (ed.field.displayField && ed.field.name) {
56758                         ed.field.el.dom.value = r.data[ed.field.name];
56759                     }
56760                     
56761                     
56762                 }).defer(50, this);
56763             }
56764         }
56765     },
56766         
56767     /**
56768      * Stops any active editing
56769      */
56770     stopEditing : function(){
56771         if(this.activeEditor){
56772             this.activeEditor.completeEdit();
56773         }
56774         this.activeEditor = null;
56775     },
56776         
56777          /**
56778      * Called to get grid's drag proxy text, by default returns this.ddText.
56779      * @return {String}
56780      */
56781     getDragDropText : function(){
56782         var count = this.selModel.getSelectedCell() ? 1 : 0;
56783         return String.format(this.ddText, count, count == 1 ? '' : 's');
56784     }
56785         
56786 });/*
56787  * Based on:
56788  * Ext JS Library 1.1.1
56789  * Copyright(c) 2006-2007, Ext JS, LLC.
56790  *
56791  * Originally Released Under LGPL - original licence link has changed is not relivant.
56792  *
56793  * Fork - LGPL
56794  * <script type="text/javascript">
56795  */
56796
56797 // private - not really -- you end up using it !
56798 // This is a support class used internally by the Grid components
56799
56800 /**
56801  * @class Roo.grid.GridEditor
56802  * @extends Roo.Editor
56803  * Class for creating and editable grid elements.
56804  * @param {Object} config any settings (must include field)
56805  */
56806 Roo.grid.GridEditor = function(field, config){
56807     if (!config && field.field) {
56808         config = field;
56809         field = Roo.factory(config.field, Roo.form);
56810     }
56811     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56812     field.monitorTab = false;
56813 };
56814
56815 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56816     
56817     /**
56818      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56819      */
56820     
56821     alignment: "tl-tl",
56822     autoSize: "width",
56823     hideEl : false,
56824     cls: "x-small-editor x-grid-editor",
56825     shim:false,
56826     shadow:"frame"
56827 });/*
56828  * Based on:
56829  * Ext JS Library 1.1.1
56830  * Copyright(c) 2006-2007, Ext JS, LLC.
56831  *
56832  * Originally Released Under LGPL - original licence link has changed is not relivant.
56833  *
56834  * Fork - LGPL
56835  * <script type="text/javascript">
56836  */
56837   
56838
56839   
56840 Roo.grid.PropertyRecord = Roo.data.Record.create([
56841     {name:'name',type:'string'},  'value'
56842 ]);
56843
56844
56845 Roo.grid.PropertyStore = function(grid, source){
56846     this.grid = grid;
56847     this.store = new Roo.data.Store({
56848         recordType : Roo.grid.PropertyRecord
56849     });
56850     this.store.on('update', this.onUpdate,  this);
56851     if(source){
56852         this.setSource(source);
56853     }
56854     Roo.grid.PropertyStore.superclass.constructor.call(this);
56855 };
56856
56857
56858
56859 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56860     setSource : function(o){
56861         this.source = o;
56862         this.store.removeAll();
56863         var data = [];
56864         for(var k in o){
56865             if(this.isEditableValue(o[k])){
56866                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56867             }
56868         }
56869         this.store.loadRecords({records: data}, {}, true);
56870     },
56871
56872     onUpdate : function(ds, record, type){
56873         if(type == Roo.data.Record.EDIT){
56874             var v = record.data['value'];
56875             var oldValue = record.modified['value'];
56876             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56877                 this.source[record.id] = v;
56878                 record.commit();
56879                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56880             }else{
56881                 record.reject();
56882             }
56883         }
56884     },
56885
56886     getProperty : function(row){
56887        return this.store.getAt(row);
56888     },
56889
56890     isEditableValue: function(val){
56891         if(val && val instanceof Date){
56892             return true;
56893         }else if(typeof val == 'object' || typeof val == 'function'){
56894             return false;
56895         }
56896         return true;
56897     },
56898
56899     setValue : function(prop, value){
56900         this.source[prop] = value;
56901         this.store.getById(prop).set('value', value);
56902     },
56903
56904     getSource : function(){
56905         return this.source;
56906     }
56907 });
56908
56909 Roo.grid.PropertyColumnModel = function(grid, store){
56910     this.grid = grid;
56911     var g = Roo.grid;
56912     g.PropertyColumnModel.superclass.constructor.call(this, [
56913         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56914         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56915     ]);
56916     this.store = store;
56917     this.bselect = Roo.DomHelper.append(document.body, {
56918         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56919             {tag: 'option', value: 'true', html: 'true'},
56920             {tag: 'option', value: 'false', html: 'false'}
56921         ]
56922     });
56923     Roo.id(this.bselect);
56924     var f = Roo.form;
56925     this.editors = {
56926         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56927         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56928         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56929         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56930         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56931     };
56932     this.renderCellDelegate = this.renderCell.createDelegate(this);
56933     this.renderPropDelegate = this.renderProp.createDelegate(this);
56934 };
56935
56936 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56937     
56938     
56939     nameText : 'Name',
56940     valueText : 'Value',
56941     
56942     dateFormat : 'm/j/Y',
56943     
56944     
56945     renderDate : function(dateVal){
56946         return dateVal.dateFormat(this.dateFormat);
56947     },
56948
56949     renderBool : function(bVal){
56950         return bVal ? 'true' : 'false';
56951     },
56952
56953     isCellEditable : function(colIndex, rowIndex){
56954         return colIndex == 1;
56955     },
56956
56957     getRenderer : function(col){
56958         return col == 1 ?
56959             this.renderCellDelegate : this.renderPropDelegate;
56960     },
56961
56962     renderProp : function(v){
56963         return this.getPropertyName(v);
56964     },
56965
56966     renderCell : function(val){
56967         var rv = val;
56968         if(val instanceof Date){
56969             rv = this.renderDate(val);
56970         }else if(typeof val == 'boolean'){
56971             rv = this.renderBool(val);
56972         }
56973         return Roo.util.Format.htmlEncode(rv);
56974     },
56975
56976     getPropertyName : function(name){
56977         var pn = this.grid.propertyNames;
56978         return pn && pn[name] ? pn[name] : name;
56979     },
56980
56981     getCellEditor : function(colIndex, rowIndex){
56982         var p = this.store.getProperty(rowIndex);
56983         var n = p.data['name'], val = p.data['value'];
56984         
56985         if(typeof(this.grid.customEditors[n]) == 'string'){
56986             return this.editors[this.grid.customEditors[n]];
56987         }
56988         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56989             return this.grid.customEditors[n];
56990         }
56991         if(val instanceof Date){
56992             return this.editors['date'];
56993         }else if(typeof val == 'number'){
56994             return this.editors['number'];
56995         }else if(typeof val == 'boolean'){
56996             return this.editors['boolean'];
56997         }else{
56998             return this.editors['string'];
56999         }
57000     }
57001 });
57002
57003 /**
57004  * @class Roo.grid.PropertyGrid
57005  * @extends Roo.grid.EditorGrid
57006  * This class represents the  interface of a component based property grid control.
57007  * <br><br>Usage:<pre><code>
57008  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57009       
57010  });
57011  // set any options
57012  grid.render();
57013  * </code></pre>
57014   
57015  * @constructor
57016  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57017  * The container MUST have some type of size defined for the grid to fill. The container will be
57018  * automatically set to position relative if it isn't already.
57019  * @param {Object} config A config object that sets properties on this grid.
57020  */
57021 Roo.grid.PropertyGrid = function(container, config){
57022     config = config || {};
57023     var store = new Roo.grid.PropertyStore(this);
57024     this.store = store;
57025     var cm = new Roo.grid.PropertyColumnModel(this, store);
57026     store.store.sort('name', 'ASC');
57027     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57028         ds: store.store,
57029         cm: cm,
57030         enableColLock:false,
57031         enableColumnMove:false,
57032         stripeRows:false,
57033         trackMouseOver: false,
57034         clicksToEdit:1
57035     }, config));
57036     this.getGridEl().addClass('x-props-grid');
57037     this.lastEditRow = null;
57038     this.on('columnresize', this.onColumnResize, this);
57039     this.addEvents({
57040          /**
57041              * @event beforepropertychange
57042              * Fires before a property changes (return false to stop?)
57043              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57044              * @param {String} id Record Id
57045              * @param {String} newval New Value
57046          * @param {String} oldval Old Value
57047              */
57048         "beforepropertychange": true,
57049         /**
57050              * @event propertychange
57051              * Fires after a property changes
57052              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57053              * @param {String} id Record Id
57054              * @param {String} newval New Value
57055          * @param {String} oldval Old Value
57056              */
57057         "propertychange": true
57058     });
57059     this.customEditors = this.customEditors || {};
57060 };
57061 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57062     
57063      /**
57064      * @cfg {Object} customEditors map of colnames=> custom editors.
57065      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57066      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57067      * false disables editing of the field.
57068          */
57069     
57070       /**
57071      * @cfg {Object} propertyNames map of property Names to their displayed value
57072          */
57073     
57074     render : function(){
57075         Roo.grid.PropertyGrid.superclass.render.call(this);
57076         this.autoSize.defer(100, this);
57077     },
57078
57079     autoSize : function(){
57080         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57081         if(this.view){
57082             this.view.fitColumns();
57083         }
57084     },
57085
57086     onColumnResize : function(){
57087         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57088         this.autoSize();
57089     },
57090     /**
57091      * Sets the data for the Grid
57092      * accepts a Key => Value object of all the elements avaiable.
57093      * @param {Object} data  to appear in grid.
57094      */
57095     setSource : function(source){
57096         this.store.setSource(source);
57097         //this.autoSize();
57098     },
57099     /**
57100      * Gets all the data from the grid.
57101      * @return {Object} data  data stored in grid
57102      */
57103     getSource : function(){
57104         return this.store.getSource();
57105     }
57106 });/*
57107   
57108  * Licence LGPL
57109  
57110  */
57111  
57112 /**
57113  * @class Roo.grid.Calendar
57114  * @extends Roo.util.Grid
57115  * This class extends the Grid to provide a calendar widget
57116  * <br><br>Usage:<pre><code>
57117  var grid = new Roo.grid.Calendar("my-container-id", {
57118      ds: myDataStore,
57119      cm: myColModel,
57120      selModel: mySelectionModel,
57121      autoSizeColumns: true,
57122      monitorWindowResize: false,
57123      trackMouseOver: true
57124      eventstore : real data store..
57125  });
57126  // set any options
57127  grid.render();
57128   
57129   * @constructor
57130  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57131  * The container MUST have some type of size defined for the grid to fill. The container will be
57132  * automatically set to position relative if it isn't already.
57133  * @param {Object} config A config object that sets properties on this grid.
57134  */
57135 Roo.grid.Calendar = function(container, config){
57136         // initialize the container
57137         this.container = Roo.get(container);
57138         this.container.update("");
57139         this.container.setStyle("overflow", "hidden");
57140     this.container.addClass('x-grid-container');
57141
57142     this.id = this.container.id;
57143
57144     Roo.apply(this, config);
57145     // check and correct shorthanded configs
57146     
57147     var rows = [];
57148     var d =1;
57149     for (var r = 0;r < 6;r++) {
57150         
57151         rows[r]=[];
57152         for (var c =0;c < 7;c++) {
57153             rows[r][c]= '';
57154         }
57155     }
57156     if (this.eventStore) {
57157         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57158         this.eventStore.on('load',this.onLoad, this);
57159         this.eventStore.on('beforeload',this.clearEvents, this);
57160          
57161     }
57162     
57163     this.dataSource = new Roo.data.Store({
57164             proxy: new Roo.data.MemoryProxy(rows),
57165             reader: new Roo.data.ArrayReader({}, [
57166                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57167     });
57168
57169     this.dataSource.load();
57170     this.ds = this.dataSource;
57171     this.ds.xmodule = this.xmodule || false;
57172     
57173     
57174     var cellRender = function(v,x,r)
57175     {
57176         return String.format(
57177             '<div class="fc-day  fc-widget-content"><div>' +
57178                 '<div class="fc-event-container"></div>' +
57179                 '<div class="fc-day-number">{0}</div>'+
57180                 
57181                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57182             '</div></div>', v);
57183     
57184     }
57185     
57186     
57187     this.colModel = new Roo.grid.ColumnModel( [
57188         {
57189             xtype: 'ColumnModel',
57190             xns: Roo.grid,
57191             dataIndex : 'weekday0',
57192             header : 'Sunday',
57193             renderer : cellRender
57194         },
57195         {
57196             xtype: 'ColumnModel',
57197             xns: Roo.grid,
57198             dataIndex : 'weekday1',
57199             header : 'Monday',
57200             renderer : cellRender
57201         },
57202         {
57203             xtype: 'ColumnModel',
57204             xns: Roo.grid,
57205             dataIndex : 'weekday2',
57206             header : 'Tuesday',
57207             renderer : cellRender
57208         },
57209         {
57210             xtype: 'ColumnModel',
57211             xns: Roo.grid,
57212             dataIndex : 'weekday3',
57213             header : 'Wednesday',
57214             renderer : cellRender
57215         },
57216         {
57217             xtype: 'ColumnModel',
57218             xns: Roo.grid,
57219             dataIndex : 'weekday4',
57220             header : 'Thursday',
57221             renderer : cellRender
57222         },
57223         {
57224             xtype: 'ColumnModel',
57225             xns: Roo.grid,
57226             dataIndex : 'weekday5',
57227             header : 'Friday',
57228             renderer : cellRender
57229         },
57230         {
57231             xtype: 'ColumnModel',
57232             xns: Roo.grid,
57233             dataIndex : 'weekday6',
57234             header : 'Saturday',
57235             renderer : cellRender
57236         }
57237     ]);
57238     this.cm = this.colModel;
57239     this.cm.xmodule = this.xmodule || false;
57240  
57241         
57242           
57243     //this.selModel = new Roo.grid.CellSelectionModel();
57244     //this.sm = this.selModel;
57245     //this.selModel.init(this);
57246     
57247     
57248     if(this.width){
57249         this.container.setWidth(this.width);
57250     }
57251
57252     if(this.height){
57253         this.container.setHeight(this.height);
57254     }
57255     /** @private */
57256         this.addEvents({
57257         // raw events
57258         /**
57259          * @event click
57260          * The raw click event for the entire grid.
57261          * @param {Roo.EventObject} e
57262          */
57263         "click" : true,
57264         /**
57265          * @event dblclick
57266          * The raw dblclick event for the entire grid.
57267          * @param {Roo.EventObject} e
57268          */
57269         "dblclick" : true,
57270         /**
57271          * @event contextmenu
57272          * The raw contextmenu event for the entire grid.
57273          * @param {Roo.EventObject} e
57274          */
57275         "contextmenu" : true,
57276         /**
57277          * @event mousedown
57278          * The raw mousedown event for the entire grid.
57279          * @param {Roo.EventObject} e
57280          */
57281         "mousedown" : true,
57282         /**
57283          * @event mouseup
57284          * The raw mouseup event for the entire grid.
57285          * @param {Roo.EventObject} e
57286          */
57287         "mouseup" : true,
57288         /**
57289          * @event mouseover
57290          * The raw mouseover event for the entire grid.
57291          * @param {Roo.EventObject} e
57292          */
57293         "mouseover" : true,
57294         /**
57295          * @event mouseout
57296          * The raw mouseout event for the entire grid.
57297          * @param {Roo.EventObject} e
57298          */
57299         "mouseout" : true,
57300         /**
57301          * @event keypress
57302          * The raw keypress event for the entire grid.
57303          * @param {Roo.EventObject} e
57304          */
57305         "keypress" : true,
57306         /**
57307          * @event keydown
57308          * The raw keydown event for the entire grid.
57309          * @param {Roo.EventObject} e
57310          */
57311         "keydown" : true,
57312
57313         // custom events
57314
57315         /**
57316          * @event cellclick
57317          * Fires when a cell is clicked
57318          * @param {Grid} this
57319          * @param {Number} rowIndex
57320          * @param {Number} columnIndex
57321          * @param {Roo.EventObject} e
57322          */
57323         "cellclick" : true,
57324         /**
57325          * @event celldblclick
57326          * Fires when a cell is double clicked
57327          * @param {Grid} this
57328          * @param {Number} rowIndex
57329          * @param {Number} columnIndex
57330          * @param {Roo.EventObject} e
57331          */
57332         "celldblclick" : true,
57333         /**
57334          * @event rowclick
57335          * Fires when a row is clicked
57336          * @param {Grid} this
57337          * @param {Number} rowIndex
57338          * @param {Roo.EventObject} e
57339          */
57340         "rowclick" : true,
57341         /**
57342          * @event rowdblclick
57343          * Fires when a row is double clicked
57344          * @param {Grid} this
57345          * @param {Number} rowIndex
57346          * @param {Roo.EventObject} e
57347          */
57348         "rowdblclick" : true,
57349         /**
57350          * @event headerclick
57351          * Fires when a header is clicked
57352          * @param {Grid} this
57353          * @param {Number} columnIndex
57354          * @param {Roo.EventObject} e
57355          */
57356         "headerclick" : true,
57357         /**
57358          * @event headerdblclick
57359          * Fires when a header cell is double clicked
57360          * @param {Grid} this
57361          * @param {Number} columnIndex
57362          * @param {Roo.EventObject} e
57363          */
57364         "headerdblclick" : true,
57365         /**
57366          * @event rowcontextmenu
57367          * Fires when a row is right clicked
57368          * @param {Grid} this
57369          * @param {Number} rowIndex
57370          * @param {Roo.EventObject} e
57371          */
57372         "rowcontextmenu" : true,
57373         /**
57374          * @event cellcontextmenu
57375          * Fires when a cell is right clicked
57376          * @param {Grid} this
57377          * @param {Number} rowIndex
57378          * @param {Number} cellIndex
57379          * @param {Roo.EventObject} e
57380          */
57381          "cellcontextmenu" : true,
57382         /**
57383          * @event headercontextmenu
57384          * Fires when a header is right clicked
57385          * @param {Grid} this
57386          * @param {Number} columnIndex
57387          * @param {Roo.EventObject} e
57388          */
57389         "headercontextmenu" : true,
57390         /**
57391          * @event bodyscroll
57392          * Fires when the body element is scrolled
57393          * @param {Number} scrollLeft
57394          * @param {Number} scrollTop
57395          */
57396         "bodyscroll" : true,
57397         /**
57398          * @event columnresize
57399          * Fires when the user resizes a column
57400          * @param {Number} columnIndex
57401          * @param {Number} newSize
57402          */
57403         "columnresize" : true,
57404         /**
57405          * @event columnmove
57406          * Fires when the user moves a column
57407          * @param {Number} oldIndex
57408          * @param {Number} newIndex
57409          */
57410         "columnmove" : true,
57411         /**
57412          * @event startdrag
57413          * Fires when row(s) start being dragged
57414          * @param {Grid} this
57415          * @param {Roo.GridDD} dd The drag drop object
57416          * @param {event} e The raw browser event
57417          */
57418         "startdrag" : true,
57419         /**
57420          * @event enddrag
57421          * Fires when a drag operation is complete
57422          * @param {Grid} this
57423          * @param {Roo.GridDD} dd The drag drop object
57424          * @param {event} e The raw browser event
57425          */
57426         "enddrag" : true,
57427         /**
57428          * @event dragdrop
57429          * Fires when dragged row(s) are dropped on a valid DD target
57430          * @param {Grid} this
57431          * @param {Roo.GridDD} dd The drag drop object
57432          * @param {String} targetId The target drag drop object
57433          * @param {event} e The raw browser event
57434          */
57435         "dragdrop" : true,
57436         /**
57437          * @event dragover
57438          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57439          * @param {Grid} this
57440          * @param {Roo.GridDD} dd The drag drop object
57441          * @param {String} targetId The target drag drop object
57442          * @param {event} e The raw browser event
57443          */
57444         "dragover" : true,
57445         /**
57446          * @event dragenter
57447          *  Fires when the dragged row(s) first cross another DD target while being dragged
57448          * @param {Grid} this
57449          * @param {Roo.GridDD} dd The drag drop object
57450          * @param {String} targetId The target drag drop object
57451          * @param {event} e The raw browser event
57452          */
57453         "dragenter" : true,
57454         /**
57455          * @event dragout
57456          * Fires when the dragged row(s) leave another DD target while being dragged
57457          * @param {Grid} this
57458          * @param {Roo.GridDD} dd The drag drop object
57459          * @param {String} targetId The target drag drop object
57460          * @param {event} e The raw browser event
57461          */
57462         "dragout" : true,
57463         /**
57464          * @event rowclass
57465          * Fires when a row is rendered, so you can change add a style to it.
57466          * @param {GridView} gridview   The grid view
57467          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57468          */
57469         'rowclass' : true,
57470
57471         /**
57472          * @event render
57473          * Fires when the grid is rendered
57474          * @param {Grid} grid
57475          */
57476         'render' : true,
57477             /**
57478              * @event select
57479              * Fires when a date is selected
57480              * @param {DatePicker} this
57481              * @param {Date} date The selected date
57482              */
57483         'select': true,
57484         /**
57485              * @event monthchange
57486              * Fires when the displayed month changes 
57487              * @param {DatePicker} this
57488              * @param {Date} date The selected month
57489              */
57490         'monthchange': true,
57491         /**
57492              * @event evententer
57493              * Fires when mouse over an event
57494              * @param {Calendar} this
57495              * @param {event} Event
57496              */
57497         'evententer': true,
57498         /**
57499              * @event eventleave
57500              * Fires when the mouse leaves an
57501              * @param {Calendar} this
57502              * @param {event}
57503              */
57504         'eventleave': true,
57505         /**
57506              * @event eventclick
57507              * Fires when the mouse click an
57508              * @param {Calendar} this
57509              * @param {event}
57510              */
57511         'eventclick': true,
57512         /**
57513              * @event eventrender
57514              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57515              * @param {Calendar} this
57516              * @param {data} data to be modified
57517              */
57518         'eventrender': true
57519         
57520     });
57521
57522     Roo.grid.Grid.superclass.constructor.call(this);
57523     this.on('render', function() {
57524         this.view.el.addClass('x-grid-cal'); 
57525         
57526         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57527
57528     },this);
57529     
57530     if (!Roo.grid.Calendar.style) {
57531         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57532             
57533             
57534             '.x-grid-cal .x-grid-col' :  {
57535                 height: 'auto !important',
57536                 'vertical-align': 'top'
57537             },
57538             '.x-grid-cal  .fc-event-hori' : {
57539                 height: '14px'
57540             }
57541              
57542             
57543         }, Roo.id());
57544     }
57545
57546     
57547     
57548 };
57549 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57550     /**
57551      * @cfg {Store} eventStore The store that loads events.
57552      */
57553     eventStore : 25,
57554
57555      
57556     activeDate : false,
57557     startDay : 0,
57558     autoWidth : true,
57559     monitorWindowResize : false,
57560
57561     
57562     resizeColumns : function() {
57563         var col = (this.view.el.getWidth() / 7) - 3;
57564         // loop through cols, and setWidth
57565         for(var i =0 ; i < 7 ; i++){
57566             this.cm.setColumnWidth(i, col);
57567         }
57568     },
57569      setDate :function(date) {
57570         
57571         Roo.log('setDate?');
57572         
57573         this.resizeColumns();
57574         var vd = this.activeDate;
57575         this.activeDate = date;
57576 //        if(vd && this.el){
57577 //            var t = date.getTime();
57578 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57579 //                Roo.log('using add remove');
57580 //                
57581 //                this.fireEvent('monthchange', this, date);
57582 //                
57583 //                this.cells.removeClass("fc-state-highlight");
57584 //                this.cells.each(function(c){
57585 //                   if(c.dateValue == t){
57586 //                       c.addClass("fc-state-highlight");
57587 //                       setTimeout(function(){
57588 //                            try{c.dom.firstChild.focus();}catch(e){}
57589 //                       }, 50);
57590 //                       return false;
57591 //                   }
57592 //                   return true;
57593 //                });
57594 //                return;
57595 //            }
57596 //        }
57597         
57598         var days = date.getDaysInMonth();
57599         
57600         var firstOfMonth = date.getFirstDateOfMonth();
57601         var startingPos = firstOfMonth.getDay()-this.startDay;
57602         
57603         if(startingPos < this.startDay){
57604             startingPos += 7;
57605         }
57606         
57607         var pm = date.add(Date.MONTH, -1);
57608         var prevStart = pm.getDaysInMonth()-startingPos;
57609 //        
57610         
57611         
57612         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57613         
57614         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57615         //this.cells.addClassOnOver('fc-state-hover');
57616         
57617         var cells = this.cells.elements;
57618         var textEls = this.textNodes;
57619         
57620         //Roo.each(cells, function(cell){
57621         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57622         //});
57623         
57624         days += startingPos;
57625
57626         // convert everything to numbers so it's fast
57627         var day = 86400000;
57628         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57629         //Roo.log(d);
57630         //Roo.log(pm);
57631         //Roo.log(prevStart);
57632         
57633         var today = new Date().clearTime().getTime();
57634         var sel = date.clearTime().getTime();
57635         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57636         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57637         var ddMatch = this.disabledDatesRE;
57638         var ddText = this.disabledDatesText;
57639         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57640         var ddaysText = this.disabledDaysText;
57641         var format = this.format;
57642         
57643         var setCellClass = function(cal, cell){
57644             
57645             //Roo.log('set Cell Class');
57646             cell.title = "";
57647             var t = d.getTime();
57648             
57649             //Roo.log(d);
57650             
57651             
57652             cell.dateValue = t;
57653             if(t == today){
57654                 cell.className += " fc-today";
57655                 cell.className += " fc-state-highlight";
57656                 cell.title = cal.todayText;
57657             }
57658             if(t == sel){
57659                 // disable highlight in other month..
57660                 cell.className += " fc-state-highlight";
57661                 
57662             }
57663             // disabling
57664             if(t < min) {
57665                 //cell.className = " fc-state-disabled";
57666                 cell.title = cal.minText;
57667                 return;
57668             }
57669             if(t > max) {
57670                 //cell.className = " fc-state-disabled";
57671                 cell.title = cal.maxText;
57672                 return;
57673             }
57674             if(ddays){
57675                 if(ddays.indexOf(d.getDay()) != -1){
57676                     // cell.title = ddaysText;
57677                    // cell.className = " fc-state-disabled";
57678                 }
57679             }
57680             if(ddMatch && format){
57681                 var fvalue = d.dateFormat(format);
57682                 if(ddMatch.test(fvalue)){
57683                     cell.title = ddText.replace("%0", fvalue);
57684                    cell.className = " fc-state-disabled";
57685                 }
57686             }
57687             
57688             if (!cell.initialClassName) {
57689                 cell.initialClassName = cell.dom.className;
57690             }
57691             
57692             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57693         };
57694
57695         var i = 0;
57696         
57697         for(; i < startingPos; i++) {
57698             cells[i].dayName =  (++prevStart);
57699             Roo.log(textEls[i]);
57700             d.setDate(d.getDate()+1);
57701             
57702             //cells[i].className = "fc-past fc-other-month";
57703             setCellClass(this, cells[i]);
57704         }
57705         
57706         var intDay = 0;
57707         
57708         for(; i < days; i++){
57709             intDay = i - startingPos + 1;
57710             cells[i].dayName =  (intDay);
57711             d.setDate(d.getDate()+1);
57712             
57713             cells[i].className = ''; // "x-date-active";
57714             setCellClass(this, cells[i]);
57715         }
57716         var extraDays = 0;
57717         
57718         for(; i < 42; i++) {
57719             //textEls[i].innerHTML = (++extraDays);
57720             
57721             d.setDate(d.getDate()+1);
57722             cells[i].dayName = (++extraDays);
57723             cells[i].className = "fc-future fc-other-month";
57724             setCellClass(this, cells[i]);
57725         }
57726         
57727         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57728         
57729         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57730         
57731         // this will cause all the cells to mis
57732         var rows= [];
57733         var i =0;
57734         for (var r = 0;r < 6;r++) {
57735             for (var c =0;c < 7;c++) {
57736                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57737             }    
57738         }
57739         
57740         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57741         for(i=0;i<cells.length;i++) {
57742             
57743             this.cells.elements[i].dayName = cells[i].dayName ;
57744             this.cells.elements[i].className = cells[i].className;
57745             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57746             this.cells.elements[i].title = cells[i].title ;
57747             this.cells.elements[i].dateValue = cells[i].dateValue ;
57748         }
57749         
57750         
57751         
57752         
57753         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57754         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57755         
57756         ////if(totalRows != 6){
57757             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57758            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57759        // }
57760         
57761         this.fireEvent('monthchange', this, date);
57762         
57763         
57764     },
57765  /**
57766      * Returns the grid's SelectionModel.
57767      * @return {SelectionModel}
57768      */
57769     getSelectionModel : function(){
57770         if(!this.selModel){
57771             this.selModel = new Roo.grid.CellSelectionModel();
57772         }
57773         return this.selModel;
57774     },
57775
57776     load: function() {
57777         this.eventStore.load()
57778         
57779         
57780         
57781     },
57782     
57783     findCell : function(dt) {
57784         dt = dt.clearTime().getTime();
57785         var ret = false;
57786         this.cells.each(function(c){
57787             //Roo.log("check " +c.dateValue + '?=' + dt);
57788             if(c.dateValue == dt){
57789                 ret = c;
57790                 return false;
57791             }
57792             return true;
57793         });
57794         
57795         return ret;
57796     },
57797     
57798     findCells : function(rec) {
57799         var s = rec.data.start_dt.clone().clearTime().getTime();
57800        // Roo.log(s);
57801         var e= rec.data.end_dt.clone().clearTime().getTime();
57802        // Roo.log(e);
57803         var ret = [];
57804         this.cells.each(function(c){
57805              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57806             
57807             if(c.dateValue > e){
57808                 return ;
57809             }
57810             if(c.dateValue < s){
57811                 return ;
57812             }
57813             ret.push(c);
57814         });
57815         
57816         return ret;    
57817     },
57818     
57819     findBestRow: function(cells)
57820     {
57821         var ret = 0;
57822         
57823         for (var i =0 ; i < cells.length;i++) {
57824             ret  = Math.max(cells[i].rows || 0,ret);
57825         }
57826         return ret;
57827         
57828     },
57829     
57830     
57831     addItem : function(rec)
57832     {
57833         // look for vertical location slot in
57834         var cells = this.findCells(rec);
57835         
57836         rec.row = this.findBestRow(cells);
57837         
57838         // work out the location.
57839         
57840         var crow = false;
57841         var rows = [];
57842         for(var i =0; i < cells.length; i++) {
57843             if (!crow) {
57844                 crow = {
57845                     start : cells[i],
57846                     end :  cells[i]
57847                 };
57848                 continue;
57849             }
57850             if (crow.start.getY() == cells[i].getY()) {
57851                 // on same row.
57852                 crow.end = cells[i];
57853                 continue;
57854             }
57855             // different row.
57856             rows.push(crow);
57857             crow = {
57858                 start: cells[i],
57859                 end : cells[i]
57860             };
57861             
57862         }
57863         
57864         rows.push(crow);
57865         rec.els = [];
57866         rec.rows = rows;
57867         rec.cells = cells;
57868         for (var i = 0; i < cells.length;i++) {
57869             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57870             
57871         }
57872         
57873         
57874     },
57875     
57876     clearEvents: function() {
57877         
57878         if (!this.eventStore.getCount()) {
57879             return;
57880         }
57881         // reset number of rows in cells.
57882         Roo.each(this.cells.elements, function(c){
57883             c.rows = 0;
57884         });
57885         
57886         this.eventStore.each(function(e) {
57887             this.clearEvent(e);
57888         },this);
57889         
57890     },
57891     
57892     clearEvent : function(ev)
57893     {
57894         if (ev.els) {
57895             Roo.each(ev.els, function(el) {
57896                 el.un('mouseenter' ,this.onEventEnter, this);
57897                 el.un('mouseleave' ,this.onEventLeave, this);
57898                 el.remove();
57899             },this);
57900             ev.els = [];
57901         }
57902     },
57903     
57904     
57905     renderEvent : function(ev,ctr) {
57906         if (!ctr) {
57907              ctr = this.view.el.select('.fc-event-container',true).first();
57908         }
57909         
57910          
57911         this.clearEvent(ev);
57912             //code
57913        
57914         
57915         
57916         ev.els = [];
57917         var cells = ev.cells;
57918         var rows = ev.rows;
57919         this.fireEvent('eventrender', this, ev);
57920         
57921         for(var i =0; i < rows.length; i++) {
57922             
57923             cls = '';
57924             if (i == 0) {
57925                 cls += ' fc-event-start';
57926             }
57927             if ((i+1) == rows.length) {
57928                 cls += ' fc-event-end';
57929             }
57930             
57931             //Roo.log(ev.data);
57932             // how many rows should it span..
57933             var cg = this.eventTmpl.append(ctr,Roo.apply({
57934                 fccls : cls
57935                 
57936             }, ev.data) , true);
57937             
57938             
57939             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57940             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57941             cg.on('click', this.onEventClick, this, ev);
57942             
57943             ev.els.push(cg);
57944             
57945             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57946             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57947             //Roo.log(cg);
57948              
57949             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57950             cg.setWidth(ebox.right - sbox.x -2);
57951         }
57952     },
57953     
57954     renderEvents: function()
57955     {   
57956         // first make sure there is enough space..
57957         
57958         if (!this.eventTmpl) {
57959             this.eventTmpl = new Roo.Template(
57960                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57961                     '<div class="fc-event-inner">' +
57962                         '<span class="fc-event-time">{time}</span>' +
57963                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57964                     '</div>' +
57965                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57966                 '</div>'
57967             );
57968                 
57969         }
57970                
57971         
57972         
57973         this.cells.each(function(c) {
57974             //Roo.log(c.select('.fc-day-content div',true).first());
57975             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57976         });
57977         
57978         var ctr = this.view.el.select('.fc-event-container',true).first();
57979         
57980         var cls;
57981         this.eventStore.each(function(ev){
57982             
57983             this.renderEvent(ev);
57984              
57985              
57986         }, this);
57987         this.view.layout();
57988         
57989     },
57990     
57991     onEventEnter: function (e, el,event,d) {
57992         this.fireEvent('evententer', this, el, event);
57993     },
57994     
57995     onEventLeave: function (e, el,event,d) {
57996         this.fireEvent('eventleave', this, el, event);
57997     },
57998     
57999     onEventClick: function (e, el,event,d) {
58000         this.fireEvent('eventclick', this, el, event);
58001     },
58002     
58003     onMonthChange: function () {
58004         this.store.load();
58005     },
58006     
58007     onLoad: function () {
58008         
58009         //Roo.log('calendar onload');
58010 //         
58011         if(this.eventStore.getCount() > 0){
58012             
58013            
58014             
58015             this.eventStore.each(function(d){
58016                 
58017                 
58018                 // FIXME..
58019                 var add =   d.data;
58020                 if (typeof(add.end_dt) == 'undefined')  {
58021                     Roo.log("Missing End time in calendar data: ");
58022                     Roo.log(d);
58023                     return;
58024                 }
58025                 if (typeof(add.start_dt) == 'undefined')  {
58026                     Roo.log("Missing Start time in calendar data: ");
58027                     Roo.log(d);
58028                     return;
58029                 }
58030                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58031                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58032                 add.id = add.id || d.id;
58033                 add.title = add.title || '??';
58034                 
58035                 this.addItem(d);
58036                 
58037              
58038             },this);
58039         }
58040         
58041         this.renderEvents();
58042     }
58043     
58044
58045 });
58046 /*
58047  grid : {
58048                 xtype: 'Grid',
58049                 xns: Roo.grid,
58050                 listeners : {
58051                     render : function ()
58052                     {
58053                         _this.grid = this;
58054                         
58055                         if (!this.view.el.hasClass('course-timesheet')) {
58056                             this.view.el.addClass('course-timesheet');
58057                         }
58058                         if (this.tsStyle) {
58059                             this.ds.load({});
58060                             return; 
58061                         }
58062                         Roo.log('width');
58063                         Roo.log(_this.grid.view.el.getWidth());
58064                         
58065                         
58066                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58067                             '.course-timesheet .x-grid-row' : {
58068                                 height: '80px'
58069                             },
58070                             '.x-grid-row td' : {
58071                                 'vertical-align' : 0
58072                             },
58073                             '.course-edit-link' : {
58074                                 'color' : 'blue',
58075                                 'text-overflow' : 'ellipsis',
58076                                 'overflow' : 'hidden',
58077                                 'white-space' : 'nowrap',
58078                                 'cursor' : 'pointer'
58079                             },
58080                             '.sub-link' : {
58081                                 'color' : 'green'
58082                             },
58083                             '.de-act-sup-link' : {
58084                                 'color' : 'purple',
58085                                 'text-decoration' : 'line-through'
58086                             },
58087                             '.de-act-link' : {
58088                                 'color' : 'red',
58089                                 'text-decoration' : 'line-through'
58090                             },
58091                             '.course-timesheet .course-highlight' : {
58092                                 'border-top-style': 'dashed !important',
58093                                 'border-bottom-bottom': 'dashed !important'
58094                             },
58095                             '.course-timesheet .course-item' : {
58096                                 'font-family'   : 'tahoma, arial, helvetica',
58097                                 'font-size'     : '11px',
58098                                 'overflow'      : 'hidden',
58099                                 'padding-left'  : '10px',
58100                                 'padding-right' : '10px',
58101                                 'padding-top' : '10px' 
58102                             }
58103                             
58104                         }, Roo.id());
58105                                 this.ds.load({});
58106                     }
58107                 },
58108                 autoWidth : true,
58109                 monitorWindowResize : false,
58110                 cellrenderer : function(v,x,r)
58111                 {
58112                     return v;
58113                 },
58114                 sm : {
58115                     xtype: 'CellSelectionModel',
58116                     xns: Roo.grid
58117                 },
58118                 dataSource : {
58119                     xtype: 'Store',
58120                     xns: Roo.data,
58121                     listeners : {
58122                         beforeload : function (_self, options)
58123                         {
58124                             options.params = options.params || {};
58125                             options.params._month = _this.monthField.getValue();
58126                             options.params.limit = 9999;
58127                             options.params['sort'] = 'when_dt';    
58128                             options.params['dir'] = 'ASC';    
58129                             this.proxy.loadResponse = this.loadResponse;
58130                             Roo.log("load?");
58131                             //this.addColumns();
58132                         },
58133                         load : function (_self, records, options)
58134                         {
58135                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58136                                 // if you click on the translation.. you can edit it...
58137                                 var el = Roo.get(this);
58138                                 var id = el.dom.getAttribute('data-id');
58139                                 var d = el.dom.getAttribute('data-date');
58140                                 var t = el.dom.getAttribute('data-time');
58141                                 //var id = this.child('span').dom.textContent;
58142                                 
58143                                 //Roo.log(this);
58144                                 Pman.Dialog.CourseCalendar.show({
58145                                     id : id,
58146                                     when_d : d,
58147                                     when_t : t,
58148                                     productitem_active : id ? 1 : 0
58149                                 }, function() {
58150                                     _this.grid.ds.load({});
58151                                 });
58152                            
58153                            });
58154                            
58155                            _this.panel.fireEvent('resize', [ '', '' ]);
58156                         }
58157                     },
58158                     loadResponse : function(o, success, response){
58159                             // this is overridden on before load..
58160                             
58161                             Roo.log("our code?");       
58162                             //Roo.log(success);
58163                             //Roo.log(response)
58164                             delete this.activeRequest;
58165                             if(!success){
58166                                 this.fireEvent("loadexception", this, o, response);
58167                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58168                                 return;
58169                             }
58170                             var result;
58171                             try {
58172                                 result = o.reader.read(response);
58173                             }catch(e){
58174                                 Roo.log("load exception?");
58175                                 this.fireEvent("loadexception", this, o, response, e);
58176                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58177                                 return;
58178                             }
58179                             Roo.log("ready...");        
58180                             // loop through result.records;
58181                             // and set this.tdate[date] = [] << array of records..
58182                             _this.tdata  = {};
58183                             Roo.each(result.records, function(r){
58184                                 //Roo.log(r.data);
58185                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58186                                     _this.tdata[r.data.when_dt.format('j')] = [];
58187                                 }
58188                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58189                             });
58190                             
58191                             //Roo.log(_this.tdata);
58192                             
58193                             result.records = [];
58194                             result.totalRecords = 6;
58195                     
58196                             // let's generate some duumy records for the rows.
58197                             //var st = _this.dateField.getValue();
58198                             
58199                             // work out monday..
58200                             //st = st.add(Date.DAY, -1 * st.format('w'));
58201                             
58202                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58203                             
58204                             var firstOfMonth = date.getFirstDayOfMonth();
58205                             var days = date.getDaysInMonth();
58206                             var d = 1;
58207                             var firstAdded = false;
58208                             for (var i = 0; i < result.totalRecords ; i++) {
58209                                 //var d= st.add(Date.DAY, i);
58210                                 var row = {};
58211                                 var added = 0;
58212                                 for(var w = 0 ; w < 7 ; w++){
58213                                     if(!firstAdded && firstOfMonth != w){
58214                                         continue;
58215                                     }
58216                                     if(d > days){
58217                                         continue;
58218                                     }
58219                                     firstAdded = true;
58220                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58221                                     row['weekday'+w] = String.format(
58222                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58223                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58224                                                     d,
58225                                                     date.format('Y-m-')+dd
58226                                                 );
58227                                     added++;
58228                                     if(typeof(_this.tdata[d]) != 'undefined'){
58229                                         Roo.each(_this.tdata[d], function(r){
58230                                             var is_sub = '';
58231                                             var deactive = '';
58232                                             var id = r.id;
58233                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58234                                             if(r.parent_id*1>0){
58235                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58236                                                 id = r.parent_id;
58237                                             }
58238                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58239                                                 deactive = 'de-act-link';
58240                                             }
58241                                             
58242                                             row['weekday'+w] += String.format(
58243                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58244                                                     id, //0
58245                                                     r.product_id_name, //1
58246                                                     r.when_dt.format('h:ia'), //2
58247                                                     is_sub, //3
58248                                                     deactive, //4
58249                                                     desc // 5
58250                                             );
58251                                         });
58252                                     }
58253                                     d++;
58254                                 }
58255                                 
58256                                 // only do this if something added..
58257                                 if(added > 0){ 
58258                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58259                                 }
58260                                 
58261                                 
58262                                 // push it twice. (second one with an hour..
58263                                 
58264                             }
58265                             //Roo.log(result);
58266                             this.fireEvent("load", this, o, o.request.arg);
58267                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58268                         },
58269                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58270                     proxy : {
58271                         xtype: 'HttpProxy',
58272                         xns: Roo.data,
58273                         method : 'GET',
58274                         url : baseURL + '/Roo/Shop_course.php'
58275                     },
58276                     reader : {
58277                         xtype: 'JsonReader',
58278                         xns: Roo.data,
58279                         id : 'id',
58280                         fields : [
58281                             {
58282                                 'name': 'id',
58283                                 'type': 'int'
58284                             },
58285                             {
58286                                 'name': 'when_dt',
58287                                 'type': 'string'
58288                             },
58289                             {
58290                                 'name': 'end_dt',
58291                                 'type': 'string'
58292                             },
58293                             {
58294                                 'name': 'parent_id',
58295                                 'type': 'int'
58296                             },
58297                             {
58298                                 'name': 'product_id',
58299                                 'type': 'int'
58300                             },
58301                             {
58302                                 'name': 'productitem_id',
58303                                 'type': 'int'
58304                             },
58305                             {
58306                                 'name': 'guid',
58307                                 'type': 'int'
58308                             }
58309                         ]
58310                     }
58311                 },
58312                 toolbar : {
58313                     xtype: 'Toolbar',
58314                     xns: Roo,
58315                     items : [
58316                         {
58317                             xtype: 'Button',
58318                             xns: Roo.Toolbar,
58319                             listeners : {
58320                                 click : function (_self, e)
58321                                 {
58322                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58323                                     sd.setMonth(sd.getMonth()-1);
58324                                     _this.monthField.setValue(sd.format('Y-m-d'));
58325                                     _this.grid.ds.load({});
58326                                 }
58327                             },
58328                             text : "Back"
58329                         },
58330                         {
58331                             xtype: 'Separator',
58332                             xns: Roo.Toolbar
58333                         },
58334                         {
58335                             xtype: 'MonthField',
58336                             xns: Roo.form,
58337                             listeners : {
58338                                 render : function (_self)
58339                                 {
58340                                     _this.monthField = _self;
58341                                    // _this.monthField.set  today
58342                                 },
58343                                 select : function (combo, date)
58344                                 {
58345                                     _this.grid.ds.load({});
58346                                 }
58347                             },
58348                             value : (function() { return new Date(); })()
58349                         },
58350                         {
58351                             xtype: 'Separator',
58352                             xns: Roo.Toolbar
58353                         },
58354                         {
58355                             xtype: 'TextItem',
58356                             xns: Roo.Toolbar,
58357                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58358                         },
58359                         {
58360                             xtype: 'Fill',
58361                             xns: Roo.Toolbar
58362                         },
58363                         {
58364                             xtype: 'Button',
58365                             xns: Roo.Toolbar,
58366                             listeners : {
58367                                 click : function (_self, e)
58368                                 {
58369                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58370                                     sd.setMonth(sd.getMonth()+1);
58371                                     _this.monthField.setValue(sd.format('Y-m-d'));
58372                                     _this.grid.ds.load({});
58373                                 }
58374                             },
58375                             text : "Next"
58376                         }
58377                     ]
58378                 },
58379                  
58380             }
58381         };
58382         
58383         *//*
58384  * Based on:
58385  * Ext JS Library 1.1.1
58386  * Copyright(c) 2006-2007, Ext JS, LLC.
58387  *
58388  * Originally Released Under LGPL - original licence link has changed is not relivant.
58389  *
58390  * Fork - LGPL
58391  * <script type="text/javascript">
58392  */
58393  
58394 /**
58395  * @class Roo.LoadMask
58396  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58397  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58398  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58399  * element's UpdateManager load indicator and will be destroyed after the initial load.
58400  * @constructor
58401  * Create a new LoadMask
58402  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58403  * @param {Object} config The config object
58404  */
58405 Roo.LoadMask = function(el, config){
58406     this.el = Roo.get(el);
58407     Roo.apply(this, config);
58408     if(this.store){
58409         this.store.on('beforeload', this.onBeforeLoad, this);
58410         this.store.on('load', this.onLoad, this);
58411         this.store.on('loadexception', this.onLoadException, this);
58412         this.removeMask = false;
58413     }else{
58414         var um = this.el.getUpdateManager();
58415         um.showLoadIndicator = false; // disable the default indicator
58416         um.on('beforeupdate', this.onBeforeLoad, this);
58417         um.on('update', this.onLoad, this);
58418         um.on('failure', this.onLoad, this);
58419         this.removeMask = true;
58420     }
58421 };
58422
58423 Roo.LoadMask.prototype = {
58424     /**
58425      * @cfg {Boolean} removeMask
58426      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58427      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58428      */
58429     /**
58430      * @cfg {String} msg
58431      * The text to display in a centered loading message box (defaults to 'Loading...')
58432      */
58433     msg : 'Loading...',
58434     /**
58435      * @cfg {String} msgCls
58436      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58437      */
58438     msgCls : 'x-mask-loading',
58439
58440     /**
58441      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58442      * @type Boolean
58443      */
58444     disabled: false,
58445
58446     /**
58447      * Disables the mask to prevent it from being displayed
58448      */
58449     disable : function(){
58450        this.disabled = true;
58451     },
58452
58453     /**
58454      * Enables the mask so that it can be displayed
58455      */
58456     enable : function(){
58457         this.disabled = false;
58458     },
58459     
58460     onLoadException : function()
58461     {
58462         Roo.log(arguments);
58463         
58464         if (typeof(arguments[3]) != 'undefined') {
58465             Roo.MessageBox.alert("Error loading",arguments[3]);
58466         } 
58467         /*
58468         try {
58469             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58470                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58471             }   
58472         } catch(e) {
58473             
58474         }
58475         */
58476     
58477         
58478         
58479         this.el.unmask(this.removeMask);
58480     },
58481     // private
58482     onLoad : function()
58483     {
58484         this.el.unmask(this.removeMask);
58485     },
58486
58487     // private
58488     onBeforeLoad : function(){
58489         if(!this.disabled){
58490             this.el.mask(this.msg, this.msgCls);
58491         }
58492     },
58493
58494     // private
58495     destroy : function(){
58496         if(this.store){
58497             this.store.un('beforeload', this.onBeforeLoad, this);
58498             this.store.un('load', this.onLoad, this);
58499             this.store.un('loadexception', this.onLoadException, this);
58500         }else{
58501             var um = this.el.getUpdateManager();
58502             um.un('beforeupdate', this.onBeforeLoad, this);
58503             um.un('update', this.onLoad, this);
58504             um.un('failure', this.onLoad, this);
58505         }
58506     }
58507 };/*
58508  * Based on:
58509  * Ext JS Library 1.1.1
58510  * Copyright(c) 2006-2007, Ext JS, LLC.
58511  *
58512  * Originally Released Under LGPL - original licence link has changed is not relivant.
58513  *
58514  * Fork - LGPL
58515  * <script type="text/javascript">
58516  */
58517
58518
58519 /**
58520  * @class Roo.XTemplate
58521  * @extends Roo.Template
58522  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58523 <pre><code>
58524 var t = new Roo.XTemplate(
58525         '&lt;select name="{name}"&gt;',
58526                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58527         '&lt;/select&gt;'
58528 );
58529  
58530 // then append, applying the master template values
58531  </code></pre>
58532  *
58533  * Supported features:
58534  *
58535  *  Tags:
58536
58537 <pre><code>
58538       {a_variable} - output encoded.
58539       {a_variable.format:("Y-m-d")} - call a method on the variable
58540       {a_variable:raw} - unencoded output
58541       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58542       {a_variable:this.method_on_template(...)} - call a method on the template object.
58543  
58544 </code></pre>
58545  *  The tpl tag:
58546 <pre><code>
58547         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58548         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58549         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58550         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58551   
58552         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58553         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58554 </code></pre>
58555  *      
58556  */
58557 Roo.XTemplate = function()
58558 {
58559     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58560     if (this.html) {
58561         this.compile();
58562     }
58563 };
58564
58565
58566 Roo.extend(Roo.XTemplate, Roo.Template, {
58567
58568     /**
58569      * The various sub templates
58570      */
58571     tpls : false,
58572     /**
58573      *
58574      * basic tag replacing syntax
58575      * WORD:WORD()
58576      *
58577      * // you can fake an object call by doing this
58578      *  x.t:(test,tesT) 
58579      * 
58580      */
58581     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58582
58583     /**
58584      * compile the template
58585      *
58586      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58587      *
58588      */
58589     compile: function()
58590     {
58591         var s = this.html;
58592      
58593         s = ['<tpl>', s, '</tpl>'].join('');
58594     
58595         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58596             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58597             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58598             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58599             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58600             m,
58601             id     = 0,
58602             tpls   = [];
58603     
58604         while(true == !!(m = s.match(re))){
58605             var forMatch   = m[0].match(nameRe),
58606                 ifMatch   = m[0].match(ifRe),
58607                 execMatch   = m[0].match(execRe),
58608                 namedMatch   = m[0].match(namedRe),
58609                 
58610                 exp  = null, 
58611                 fn   = null,
58612                 exec = null,
58613                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58614                 
58615             if (ifMatch) {
58616                 // if - puts fn into test..
58617                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58618                 if(exp){
58619                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58620                 }
58621             }
58622             
58623             if (execMatch) {
58624                 // exec - calls a function... returns empty if true is  returned.
58625                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58626                 if(exp){
58627                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58628                 }
58629             }
58630             
58631             
58632             if (name) {
58633                 // for = 
58634                 switch(name){
58635                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58636                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58637                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58638                 }
58639             }
58640             var uid = namedMatch ? namedMatch[1] : id;
58641             
58642             
58643             tpls.push({
58644                 id:     namedMatch ? namedMatch[1] : id,
58645                 target: name,
58646                 exec:   exec,
58647                 test:   fn,
58648                 body:   m[1] || ''
58649             });
58650             if (namedMatch) {
58651                 s = s.replace(m[0], '');
58652             } else { 
58653                 s = s.replace(m[0], '{xtpl'+ id + '}');
58654             }
58655             ++id;
58656         }
58657         this.tpls = [];
58658         for(var i = tpls.length-1; i >= 0; --i){
58659             this.compileTpl(tpls[i]);
58660             this.tpls[tpls[i].id] = tpls[i];
58661         }
58662         this.master = tpls[tpls.length-1];
58663         return this;
58664     },
58665     /**
58666      * same as applyTemplate, except it's done to one of the subTemplates
58667      * when using named templates, you can do:
58668      *
58669      * var str = pl.applySubTemplate('your-name', values);
58670      *
58671      * 
58672      * @param {Number} id of the template
58673      * @param {Object} values to apply to template
58674      * @param {Object} parent (normaly the instance of this object)
58675      */
58676     applySubTemplate : function(id, values, parent)
58677     {
58678         
58679         
58680         var t = this.tpls[id];
58681         
58682         
58683         try { 
58684             if(t.test && !t.test.call(this, values, parent)){
58685                 return '';
58686             }
58687         } catch(e) {
58688             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58689             Roo.log(e.toString());
58690             Roo.log(t.test);
58691             return ''
58692         }
58693         try { 
58694             
58695             if(t.exec && t.exec.call(this, values, parent)){
58696                 return '';
58697             }
58698         } catch(e) {
58699             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58700             Roo.log(e.toString());
58701             Roo.log(t.exec);
58702             return ''
58703         }
58704         try {
58705             var vs = t.target ? t.target.call(this, values, parent) : values;
58706             parent = t.target ? values : parent;
58707             if(t.target && vs instanceof Array){
58708                 var buf = [];
58709                 for(var i = 0, len = vs.length; i < len; i++){
58710                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58711                 }
58712                 return buf.join('');
58713             }
58714             return t.compiled.call(this, vs, parent);
58715         } catch (e) {
58716             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58717             Roo.log(e.toString());
58718             Roo.log(t.compiled);
58719             return '';
58720         }
58721     },
58722
58723     compileTpl : function(tpl)
58724     {
58725         var fm = Roo.util.Format;
58726         var useF = this.disableFormats !== true;
58727         var sep = Roo.isGecko ? "+" : ",";
58728         var undef = function(str) {
58729             Roo.log("Property not found :"  + str);
58730             return '';
58731         };
58732         
58733         var fn = function(m, name, format, args)
58734         {
58735             //Roo.log(arguments);
58736             args = args ? args.replace(/\\'/g,"'") : args;
58737             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58738             if (typeof(format) == 'undefined') {
58739                 format= 'htmlEncode';
58740             }
58741             if (format == 'raw' ) {
58742                 format = false;
58743             }
58744             
58745             if(name.substr(0, 4) == 'xtpl'){
58746                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58747             }
58748             
58749             // build an array of options to determine if value is undefined..
58750             
58751             // basically get 'xxxx.yyyy' then do
58752             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58753             //    (function () { Roo.log("Property not found"); return ''; })() :
58754             //    ......
58755             
58756             var udef_ar = [];
58757             var lookfor = '';
58758             Roo.each(name.split('.'), function(st) {
58759                 lookfor += (lookfor.length ? '.': '') + st;
58760                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58761             });
58762             
58763             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58764             
58765             
58766             if(format && useF){
58767                 
58768                 args = args ? ',' + args : "";
58769                  
58770                 if(format.substr(0, 5) != "this."){
58771                     format = "fm." + format + '(';
58772                 }else{
58773                     format = 'this.call("'+ format.substr(5) + '", ';
58774                     args = ", values";
58775                 }
58776                 
58777                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58778             }
58779              
58780             if (args.length) {
58781                 // called with xxyx.yuu:(test,test)
58782                 // change to ()
58783                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58784             }
58785             // raw.. - :raw modifier..
58786             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58787             
58788         };
58789         var body;
58790         // branched to use + in gecko and [].join() in others
58791         if(Roo.isGecko){
58792             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58793                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58794                     "';};};";
58795         }else{
58796             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58797             body.push(tpl.body.replace(/(\r\n|\n)/g,
58798                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58799             body.push("'].join('');};};");
58800             body = body.join('');
58801         }
58802         
58803         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58804        
58805         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58806         eval(body);
58807         
58808         return this;
58809     },
58810
58811     applyTemplate : function(values){
58812         return this.master.compiled.call(this, values, {});
58813         //var s = this.subs;
58814     },
58815
58816     apply : function(){
58817         return this.applyTemplate.apply(this, arguments);
58818     }
58819
58820  });
58821
58822 Roo.XTemplate.from = function(el){
58823     el = Roo.getDom(el);
58824     return new Roo.XTemplate(el.value || el.innerHTML);
58825 };